mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Merge branch 'develop' into feature
This commit is contained in:
commit
8d53b46e82
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip install pycodestyle coverage
|
pip install pycodestyle coverage tblib
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: mkdocs build
|
run: mkdocs build
|
||||||
|
@ -87,7 +87,7 @@ mkdocs-material
|
|||||||
mkdocstrings
|
mkdocstrings
|
||||||
|
|
||||||
# Library for manipulating IP prefixes and addresses
|
# Library for manipulating IP prefixes and addresses
|
||||||
# https://github.com/drkjam/netaddr
|
# https://github.com/netaddr/netaddr
|
||||||
netaddr
|
netaddr
|
||||||
|
|
||||||
# Fork of PIL (Python Imaging Library) for image processing
|
# Fork of PIL (Python Imaging Library) for image processing
|
||||||
|
@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
## v3.1.10 (FUTURE)
|
## v3.1.10 (FUTURE)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#8457](https://github.com/netbox-community/netbox/issues/8457) - Enable adding non-racked devices from site & location views
|
||||||
|
* [#8553](https://github.com/netbox-community/netbox/issues/8553) - Add missing object types to global search form
|
||||||
|
* [#8575](https://github.com/netbox-community/netbox/issues/8575) - Add rack columns to cables list
|
||||||
|
* [#8645](https://github.com/netbox-community/netbox/issues/8645) - Enable filtering objects by assigned contacts & contact roles
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#8820](https://github.com/netbox-community/netbox/issues/8820) - Fix navbar background color in dark mode
|
||||||
|
* [#8850](https://github.com/netbox-community/netbox/issues/8850) - Show airflow field on device REST API serializer when config context data is included
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v3.1.9 (2022-03-07)
|
## v3.1.9 (2022-03-07)
|
||||||
|
@ -3,8 +3,8 @@ from django.db.models import Q
|
|||||||
|
|
||||||
from dcim.filtersets import CableTerminationFilterSet
|
from dcim.filtersets import CableTerminationFilterSet
|
||||||
from dcim.models import Region, Site, SiteGroup
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet
|
from netbox.filtersets import ChangeLoggedModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet
|
||||||
from tenancy.filtersets import TenancyFilterSet
|
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
|
||||||
from utilities.filters import TreeNodeMultipleChoiceFilter
|
from utilities.filters import TreeNodeMultipleChoiceFilter
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import *
|
from .models import *
|
||||||
@ -18,7 +18,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderFilterSet(NetBoxModelFilterSet):
|
class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='circuits__terminations__site__region',
|
field_name='circuits__terminations__site__region',
|
||||||
@ -107,7 +107,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
|
||||||
provider_id = django_filters.ModelMultipleChoiceFilter(
|
provider_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
label='Provider (ID)',
|
label='Provider (ID)',
|
||||||
|
@ -5,7 +5,7 @@ from circuits.choices import CircuitStatusChoices
|
|||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.models import Region, Site, SiteGroup
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from netbox.forms import NetBoxModelFilterSetForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
|
||||||
from utilities.forms import DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField
|
from utilities.forms import DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -16,12 +16,13 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderFilterForm(NetBoxModelFilterSetForm):
|
class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Provider
|
model = Provider
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Location', ('region_id', 'site_group_id', 'site_id')),
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
('ASN', ('asn',)),
|
('ASN', ('asn',)),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -72,7 +73,7 @@ class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class CircuitFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
@ -80,6 +81,7 @@ class CircuitFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
('Attributes', ('type_id', 'status', 'commit_rate')),
|
('Attributes', ('type_id', 'status', 'commit_rate')),
|
||||||
('Location', ('region_id', 'site_group_id', 'site_id')),
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
type_id = DynamicModelMultipleChoiceField(
|
type_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
|
@ -59,6 +59,9 @@ class CircuitTable(NetBoxTable):
|
|||||||
)
|
)
|
||||||
commit_rate = CommitRateColumn()
|
commit_rate = CommitRateColumn()
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='circuits:circuit_list'
|
url_name='circuits:circuit_list'
|
||||||
)
|
)
|
||||||
@ -67,7 +70,7 @@ class CircuitTable(NetBoxTable):
|
|||||||
model = Circuit
|
model = Circuit
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date',
|
'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date',
|
||||||
'commit_rate', 'description', 'comments', 'tags', 'created', 'last_updated',
|
'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',
|
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',
|
||||||
|
@ -19,6 +19,9 @@ class ProviderTable(NetBoxTable):
|
|||||||
verbose_name='Circuits'
|
verbose_name='Circuits'
|
||||||
)
|
)
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='circuits:provider_list'
|
url_name='circuits:provider_list'
|
||||||
)
|
)
|
||||||
@ -27,7 +30,7 @@ class ProviderTable(NetBoxTable):
|
|||||||
model = Provider
|
model = Provider
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count',
|
'pk', 'id', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count',
|
||||||
'comments', 'tags', 'created', 'last_updated',
|
'comments', 'contacts', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
||||||
|
|
||||||
|
@ -576,9 +576,9 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
|
|||||||
class Meta(DeviceSerializer.Meta):
|
class Meta(DeviceSerializer.Meta):
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
|
'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
|
||||||
'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4',
|
'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'airflow', 'primary_ip',
|
||||||
'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'local_context_data',
|
'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments',
|
||||||
'tags', 'custom_fields', 'config_context', 'created', 'last_updated',
|
'local_context_data', 'tags', 'custom_fields', 'config_context', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
@swagger_serializer_method(serializer_or_field=serializers.DictField)
|
@swagger_serializer_method(serializer_or_field=serializers.DictField)
|
||||||
|
@ -6,8 +6,8 @@ from ipam.models import ASN, VRF
|
|||||||
from netbox.filtersets import (
|
from netbox.filtersets import (
|
||||||
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet,
|
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet,
|
||||||
)
|
)
|
||||||
from tenancy.filtersets import TenancyFilterSet
|
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import *
|
||||||
from utilities.choices import ColorChoices
|
from utilities.choices import ColorChoices
|
||||||
from utilities.filters import (
|
from utilities.filters import (
|
||||||
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
|
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
|
||||||
@ -67,7 +67,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RegionFilterSet(OrganizationalModelFilterSet):
|
class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
label='Parent region (ID)',
|
label='Parent region (ID)',
|
||||||
@ -84,7 +84,7 @@ class RegionFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupFilterSet(OrganizationalModelFilterSet):
|
class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=SiteGroup.objects.all(),
|
queryset=SiteGroup.objects.all(),
|
||||||
label='Parent site group (ID)',
|
label='Parent site group (ID)',
|
||||||
@ -101,7 +101,7 @@ class SiteGroupFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=SiteStatusChoices,
|
choices=SiteStatusChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
@ -166,7 +166,7 @@ class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
|||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
|
|
||||||
|
|
||||||
class LocationFilterSet(TenancyFilterSet, OrganizationalModelFilterSet):
|
class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalModelFilterSet):
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
@ -237,7 +237,7 @@ class RackRoleFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'color', 'description']
|
fields = ['id', 'name', 'slug', 'color', 'description']
|
||||||
|
|
||||||
|
|
||||||
class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
@ -385,7 +385,7 @@ class RackReservationFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerFilterSet(OrganizationalModelFilterSet):
|
class ManufacturerFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
@ -724,7 +724,7 @@ class PlatformFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'napalm_driver', 'description']
|
fields = ['id', 'name', 'slug', 'napalm_driver', 'description']
|
||||||
|
|
||||||
|
|
||||||
class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, LocalConfigContextFilterSet):
|
class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet):
|
||||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_type__manufacturer',
|
field_name='device_type__manufacturer',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
@ -1514,7 +1514,7 @@ class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelFilterSet(NetBoxModelFilterSet):
|
class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
|
@ -8,7 +8,7 @@ from dcim.models import *
|
|||||||
from extras.forms import LocalConfigContextFilterForm
|
from extras.forms import LocalConfigContextFilterForm
|
||||||
from ipam.models import ASN, VRF
|
from ipam.models import ASN, VRF
|
||||||
from netbox.forms import NetBoxModelFilterSetForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
|
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
|
||||||
StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES, SelectSpeedWidget,
|
StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES, SelectSpeedWidget,
|
||||||
@ -104,8 +104,12 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RegionFilterForm(NetBoxModelFilterSetForm):
|
class RegionFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Region
|
model = Region
|
||||||
|
fieldsets = (
|
||||||
|
(None, ('q', 'tag', 'parent_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role'))
|
||||||
|
)
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -114,8 +118,12 @@ class RegionFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupFilterForm(NetBoxModelFilterSetForm):
|
class SiteGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = SiteGroup
|
model = SiteGroup
|
||||||
|
fieldsets = (
|
||||||
|
(None, ('q', 'tag', 'parent_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role'))
|
||||||
|
)
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=SiteGroup.objects.all(),
|
queryset=SiteGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -124,12 +132,13 @@ class SiteGroupFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class SiteFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Site
|
model = Site
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Attributes', ('status', 'region_id', 'group_id', 'asn_id')),
|
('Attributes', ('status', 'region_id', 'group_id', 'asn_id')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
status = forms.MultipleChoiceField(
|
status = forms.MultipleChoiceField(
|
||||||
choices=SiteStatusChoices,
|
choices=SiteStatusChoices,
|
||||||
@ -154,12 +163,13 @@ class SiteFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class LocationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Location
|
model = Location
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Parent', ('region_id', 'site_group_id', 'site_id', 'parent_id')),
|
('Parent', ('region_id', 'site_group_id', 'site_id', 'parent_id')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -197,7 +207,7 @@ class RackRoleFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class RackFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Rack
|
model = Rack
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
@ -205,6 +215,7 @@ class RackFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
('Function', ('status', 'role_id')),
|
('Function', ('status', 'role_id')),
|
||||||
('Hardware', ('type', 'width', 'serial', 'asset_tag')),
|
('Hardware', ('type', 'width', 'serial', 'asset_tag')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -308,8 +319,12 @@ class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerFilterForm(NetBoxModelFilterSetForm):
|
class ManufacturerFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
|
fieldsets = (
|
||||||
|
(None, ('q', 'tag')),
|
||||||
|
('Contacts', ('contact', 'contact_role'))
|
||||||
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
@ -465,7 +480,12 @@ class PlatformFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
|
class DeviceFilterForm(
|
||||||
|
LocalConfigContextFilterForm,
|
||||||
|
TenancyFilterForm,
|
||||||
|
ContactModelFilterForm,
|
||||||
|
NetBoxModelFilterSetForm
|
||||||
|
):
|
||||||
model = Device
|
model = Device
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
@ -473,6 +493,7 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxMo
|
|||||||
('Operation', ('status', 'role_id', 'airflow', 'serial', 'asset_tag', 'mac_address')),
|
('Operation', ('status', 'role_id', 'airflow', 'serial', 'asset_tag', 'mac_address')),
|
||||||
('Hardware', ('manufacturer_id', 'device_type_id', 'platform_id')),
|
('Hardware', ('manufacturer_id', 'device_type_id', 'platform_id')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
('Components', (
|
('Components', (
|
||||||
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports',
|
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports',
|
||||||
)),
|
)),
|
||||||
@ -741,11 +762,12 @@ class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelFilterForm(NetBoxModelFilterSetForm):
|
class PowerPanelFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Location', ('region_id', 'site_group_id', 'site_id', 'location_id'))
|
('Location', ('region_id', 'site_group_id', 'site_id', 'location_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
|
@ -22,6 +22,12 @@ class CableTable(NetBoxTable):
|
|||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Side A'
|
verbose_name='Side A'
|
||||||
)
|
)
|
||||||
|
rack_a = tables.Column(
|
||||||
|
accessor=Accessor('termination_a__device__rack'),
|
||||||
|
orderable=False,
|
||||||
|
linkify=True,
|
||||||
|
verbose_name='Rack A'
|
||||||
|
)
|
||||||
termination_a = tables.Column(
|
termination_a = tables.Column(
|
||||||
accessor=Accessor('termination_a'),
|
accessor=Accessor('termination_a'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
@ -34,6 +40,12 @@ class CableTable(NetBoxTable):
|
|||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Side B'
|
verbose_name='Side B'
|
||||||
)
|
)
|
||||||
|
rack_b = tables.Column(
|
||||||
|
accessor=Accessor('termination_b__device__rack'),
|
||||||
|
orderable=False,
|
||||||
|
linkify=True,
|
||||||
|
verbose_name='Rack B'
|
||||||
|
)
|
||||||
termination_b = tables.Column(
|
termination_b = tables.Column(
|
||||||
accessor=Accessor('termination_b'),
|
accessor=Accessor('termination_b'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
@ -54,7 +66,7 @@ class CableTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
|
'pk', 'id', 'label', 'termination_a_parent', 'rack_a', 'termination_a', 'termination_b_parent', 'rack_b', 'termination_b',
|
||||||
'status', 'type', 'tenant', 'color', 'length', 'tags', 'created', 'last_updated',
|
'status', 'type', 'tenant', 'color', 'length', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
|
@ -190,6 +190,9 @@ class DeviceTable(NetBoxTable):
|
|||||||
verbose_name='VC Priority'
|
verbose_name='VC Priority'
|
||||||
)
|
)
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:device_list'
|
url_name='dcim:device_list'
|
||||||
)
|
)
|
||||||
@ -199,8 +202,8 @@ class DeviceTable(NetBoxTable):
|
|||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'status', 'tenant', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial',
|
'pk', 'id', 'name', 'status', 'tenant', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial',
|
||||||
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4',
|
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4',
|
||||||
'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'created',
|
'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'contacts', 'tags',
|
||||||
'last_updated',
|
'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type',
|
'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type',
|
||||||
|
@ -41,6 +41,9 @@ class ManufacturerTable(NetBoxTable):
|
|||||||
verbose_name='Platforms'
|
verbose_name='Platforms'
|
||||||
)
|
)
|
||||||
slug = tables.Column()
|
slug = tables.Column()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:manufacturer_list'
|
url_name='dcim:manufacturer_list'
|
||||||
)
|
)
|
||||||
@ -49,7 +52,7 @@ class ManufacturerTable(NetBoxTable):
|
|||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
|
'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
|
||||||
'actions', 'created', 'last_updated',
|
'contacts', 'actions', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
|
'pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
|
||||||
|
@ -26,13 +26,16 @@ class PowerPanelTable(NetBoxTable):
|
|||||||
url_params={'power_panel_id': 'pk'},
|
url_params={'power_panel_id': 'pk'},
|
||||||
verbose_name='Feeds'
|
verbose_name='Feeds'
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:powerpanel_list'
|
url_name='dcim:powerpanel_list'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'tags', 'created', 'last_updated',)
|
fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'tags', 'created', 'last_updated',)
|
||||||
default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count')
|
default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count')
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +69,9 @@ class RackTable(NetBoxTable):
|
|||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Power'
|
verbose_name='Power'
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:rack_list'
|
url_name='dcim:rack_list'
|
||||||
)
|
)
|
||||||
@ -86,7 +89,7 @@ class RackTable(NetBoxTable):
|
|||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag',
|
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag',
|
||||||
'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization',
|
'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization',
|
||||||
'get_power_utilization', 'tags', 'created', 'last_updated',
|
'get_power_utilization', 'contacts', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
||||||
|
@ -26,6 +26,9 @@ class RegionTable(NetBoxTable):
|
|||||||
url_params={'region_id': 'pk'},
|
url_params={'region_id': 'pk'},
|
||||||
verbose_name='Sites'
|
verbose_name='Sites'
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:region_list'
|
url_name='dcim:region_list'
|
||||||
)
|
)
|
||||||
@ -33,7 +36,8 @@ class RegionTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Region
|
model = Region
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'created', 'last_updated', 'actions',
|
'pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'created', 'last_updated',
|
||||||
|
'actions',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'site_count', 'description')
|
default_columns = ('pk', 'name', 'site_count', 'description')
|
||||||
|
|
||||||
@ -51,6 +55,9 @@ class SiteGroupTable(NetBoxTable):
|
|||||||
url_params={'group_id': 'pk'},
|
url_params={'group_id': 'pk'},
|
||||||
verbose_name='Sites'
|
verbose_name='Sites'
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:sitegroup_list'
|
url_name='dcim:sitegroup_list'
|
||||||
)
|
)
|
||||||
@ -58,7 +65,8 @@ class SiteGroupTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = SiteGroup
|
model = SiteGroup
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'created', 'last_updated', 'actions',
|
'pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'created', 'last_updated',
|
||||||
|
'actions',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'site_count', 'description')
|
default_columns = ('pk', 'name', 'site_count', 'description')
|
||||||
|
|
||||||
@ -90,6 +98,9 @@ class SiteTable(NetBoxTable):
|
|||||||
)
|
)
|
||||||
tenant = TenantColumn()
|
tenant = TenantColumn()
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:site_list'
|
url_name='dcim:site_list'
|
||||||
)
|
)
|
||||||
@ -99,7 +110,7 @@ class SiteTable(NetBoxTable):
|
|||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asns', 'asn_count',
|
'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asns', 'asn_count',
|
||||||
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments',
|
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments',
|
||||||
'tags', 'created', 'last_updated', 'actions',
|
'contacts', 'tags', 'created', 'last_updated', 'actions',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description')
|
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description')
|
||||||
|
|
||||||
@ -126,6 +137,9 @@ class LocationTable(NetBoxTable):
|
|||||||
url_params={'location_id': 'pk'},
|
url_params={'location_id': 'pk'},
|
||||||
verbose_name='Devices'
|
verbose_name='Devices'
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='dcim:location_list'
|
url_name='dcim:location_list'
|
||||||
)
|
)
|
||||||
@ -136,7 +150,7 @@ class LocationTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Location
|
model = Location
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'tags',
|
'pk', 'id', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'contacts',
|
||||||
'actions', 'created', 'last_updated',
|
'tags', 'actions', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description')
|
default_columns = ('pk', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description')
|
||||||
|
@ -338,6 +338,11 @@ class SiteView(generic.ObjectView):
|
|||||||
'device_count',
|
'device_count',
|
||||||
cumulative=True
|
cumulative=True
|
||||||
).restrict(request.user, 'view').filter(site=instance)
|
).restrict(request.user, 'view').filter(site=instance)
|
||||||
|
nonracked_devices = Device.objects.filter(
|
||||||
|
site=instance,
|
||||||
|
position__isnull=True,
|
||||||
|
parent_bay__isnull=True
|
||||||
|
).prefetch_related('device_type__manufacturer')
|
||||||
|
|
||||||
asns = ASN.objects.restrict(request.user, 'view').filter(sites=instance)
|
asns = ASN.objects.restrict(request.user, 'view').filter(sites=instance)
|
||||||
asn_count = asns.count()
|
asn_count = asns.count()
|
||||||
@ -348,6 +353,7 @@ class SiteView(generic.ObjectView):
|
|||||||
'stats': stats,
|
'stats': stats,
|
||||||
'locations': locations,
|
'locations': locations,
|
||||||
'asns': asns,
|
'asns': asns,
|
||||||
|
'nonracked_devices': nonracked_devices,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -425,11 +431,17 @@ class LocationView(generic.ObjectView):
|
|||||||
).filter(pk__in=location_ids).exclude(pk=instance.pk)
|
).filter(pk__in=location_ids).exclude(pk=instance.pk)
|
||||||
child_locations_table = tables.LocationTable(child_locations)
|
child_locations_table = tables.LocationTable(child_locations)
|
||||||
child_locations_table.configure(request)
|
child_locations_table.configure(request)
|
||||||
|
nonracked_devices = Device.objects.filter(
|
||||||
|
location=instance,
|
||||||
|
position__isnull=True,
|
||||||
|
parent_bay__isnull=True
|
||||||
|
).prefetch_related('device_type__manufacturer')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'rack_count': rack_count,
|
'rack_count': rack_count,
|
||||||
'device_count': device_count,
|
'device_count': device_count,
|
||||||
'child_locations_table': child_locations_table,
|
'child_locations_table': child_locations_table,
|
||||||
|
'nonracked_devices': nonracked_devices,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
|||||||
)
|
)
|
||||||
vlan_vid = django_filters.NumberFilter(
|
vlan_vid = django_filters.NumberFilter(
|
||||||
field_name='vlan__vid',
|
field_name='vlan__vid',
|
||||||
label='VLAN number (1-4095)',
|
label='VLAN number (1-4094)',
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
|
@ -388,7 +388,7 @@ class VLANCSVForm(NetBoxModelCSVForm):
|
|||||||
model = VLAN
|
model = VLAN
|
||||||
fields = ('site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description')
|
fields = ('site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description')
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'vid': 'Numeric VLAN ID (1-4095)',
|
'vid': 'Numeric VLAN ID (1-4094)',
|
||||||
'name': 'VLAN name',
|
'name': 'VLAN name',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from circuits.filtersets import CircuitFilterSet, ProviderFilterSet, ProviderNetworkFilterSet
|
from circuits.filtersets import CircuitFilterSet, ProviderFilterSet, ProviderNetworkFilterSet
|
||||||
from circuits.models import Circuit, ProviderNetwork, Provider
|
from circuits.models import Circuit, ProviderNetwork, Provider
|
||||||
@ -26,169 +27,212 @@ from virtualization.models import Cluster, VirtualMachine
|
|||||||
from virtualization.tables import ClusterTable, VirtualMachineTable
|
from virtualization.tables import ClusterTable, VirtualMachineTable
|
||||||
|
|
||||||
SEARCH_MAX_RESULTS = 15
|
SEARCH_MAX_RESULTS = 15
|
||||||
SEARCH_TYPES = OrderedDict((
|
|
||||||
# Circuits
|
CIRCUIT_TYPES = OrderedDict(
|
||||||
('provider', {
|
(
|
||||||
'queryset': Provider.objects.annotate(
|
('provider', {
|
||||||
count_circuits=count_related(Circuit, 'provider')
|
'queryset': Provider.objects.annotate(
|
||||||
),
|
count_circuits=count_related(Circuit, 'provider')
|
||||||
'filterset': ProviderFilterSet,
|
|
||||||
'table': ProviderTable,
|
|
||||||
'url': 'circuits:provider_list',
|
|
||||||
}),
|
|
||||||
('circuit', {
|
|
||||||
'queryset': Circuit.objects.prefetch_related(
|
|
||||||
'type', 'provider', 'tenant', 'terminations__site'
|
|
||||||
),
|
|
||||||
'filterset': CircuitFilterSet,
|
|
||||||
'table': CircuitTable,
|
|
||||||
'url': 'circuits:circuit_list',
|
|
||||||
}),
|
|
||||||
('providernetwork', {
|
|
||||||
'queryset': ProviderNetwork.objects.prefetch_related('provider'),
|
|
||||||
'filterset': ProviderNetworkFilterSet,
|
|
||||||
'table': ProviderNetworkTable,
|
|
||||||
'url': 'circuits:providernetwork_list',
|
|
||||||
}),
|
|
||||||
# DCIM
|
|
||||||
('site', {
|
|
||||||
'queryset': Site.objects.prefetch_related('region', 'tenant'),
|
|
||||||
'filterset': SiteFilterSet,
|
|
||||||
'table': SiteTable,
|
|
||||||
'url': 'dcim:site_list',
|
|
||||||
}),
|
|
||||||
('rack', {
|
|
||||||
'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'role'),
|
|
||||||
'filterset': RackFilterSet,
|
|
||||||
'table': RackTable,
|
|
||||||
'url': 'dcim:rack_list',
|
|
||||||
}),
|
|
||||||
('rackreservation', {
|
|
||||||
'queryset': RackReservation.objects.prefetch_related('site', 'rack', 'user'),
|
|
||||||
'filterset': RackReservationFilterSet,
|
|
||||||
'table': RackReservationTable,
|
|
||||||
'url': 'dcim:rackreservation_list',
|
|
||||||
}),
|
|
||||||
('location', {
|
|
||||||
'queryset': Location.objects.add_related_count(
|
|
||||||
Location.objects.add_related_count(
|
|
||||||
Location.objects.all(),
|
|
||||||
Device,
|
|
||||||
'location',
|
|
||||||
'device_count',
|
|
||||||
cumulative=True
|
|
||||||
),
|
),
|
||||||
Rack,
|
'filterset': ProviderFilterSet,
|
||||||
'location',
|
'table': ProviderTable,
|
||||||
'rack_count',
|
'url': 'circuits:provider_list',
|
||||||
cumulative=True
|
}),
|
||||||
).prefetch_related('site'),
|
('circuit', {
|
||||||
'filterset': LocationFilterSet,
|
'queryset': Circuit.objects.prefetch_related(
|
||||||
'table': LocationTable,
|
'type', 'provider', 'tenant', 'terminations__site'
|
||||||
'url': 'dcim:location_list',
|
),
|
||||||
}),
|
'filterset': CircuitFilterSet,
|
||||||
('devicetype', {
|
'table': CircuitTable,
|
||||||
'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate(
|
'url': 'circuits:circuit_list',
|
||||||
instance_count=count_related(Device, 'device_type')
|
}),
|
||||||
),
|
('providernetwork', {
|
||||||
'filterset': DeviceTypeFilterSet,
|
'queryset': ProviderNetwork.objects.prefetch_related('provider'),
|
||||||
'table': DeviceTypeTable,
|
'filterset': ProviderNetworkFilterSet,
|
||||||
'url': 'dcim:devicetype_list',
|
'table': ProviderNetworkTable,
|
||||||
}),
|
'url': 'circuits:providernetwork_list',
|
||||||
('device', {
|
}),
|
||||||
'queryset': Device.objects.prefetch_related(
|
)
|
||||||
'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6',
|
)
|
||||||
),
|
|
||||||
'filterset': DeviceFilterSet,
|
|
||||||
'table': DeviceTable,
|
DCIM_TYPES = OrderedDict(
|
||||||
'url': 'dcim:device_list',
|
(
|
||||||
}),
|
('site', {
|
||||||
('virtualchassis', {
|
'queryset': Site.objects.prefetch_related('region', 'tenant'),
|
||||||
'queryset': VirtualChassis.objects.prefetch_related('master').annotate(
|
'filterset': SiteFilterSet,
|
||||||
member_count=count_related(Device, 'virtual_chassis')
|
'table': SiteTable,
|
||||||
),
|
'url': 'dcim:site_list',
|
||||||
'filterset': VirtualChassisFilterSet,
|
}),
|
||||||
'table': VirtualChassisTable,
|
('rack', {
|
||||||
'url': 'dcim:virtualchassis_list',
|
'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'role'),
|
||||||
}),
|
'filterset': RackFilterSet,
|
||||||
('cable', {
|
'table': RackTable,
|
||||||
'queryset': Cable.objects.all(),
|
'url': 'dcim:rack_list',
|
||||||
'filterset': CableFilterSet,
|
}),
|
||||||
'table': CableTable,
|
('rackreservation', {
|
||||||
'url': 'dcim:cable_list',
|
'queryset': RackReservation.objects.prefetch_related('site', 'rack', 'user'),
|
||||||
}),
|
'filterset': RackReservationFilterSet,
|
||||||
('powerfeed', {
|
'table': RackReservationTable,
|
||||||
'queryset': PowerFeed.objects.all(),
|
'url': 'dcim:rackreservation_list',
|
||||||
'filterset': PowerFeedFilterSet,
|
}),
|
||||||
'table': PowerFeedTable,
|
('location', {
|
||||||
'url': 'dcim:powerfeed_list',
|
'queryset': Location.objects.add_related_count(
|
||||||
}),
|
Location.objects.add_related_count(
|
||||||
# Virtualization
|
Location.objects.all(),
|
||||||
('cluster', {
|
Device,
|
||||||
'queryset': Cluster.objects.prefetch_related('type', 'group').annotate(
|
'location',
|
||||||
device_count=count_related(Device, 'cluster'),
|
'device_count',
|
||||||
vm_count=count_related(VirtualMachine, 'cluster')
|
cumulative=True
|
||||||
),
|
),
|
||||||
'filterset': ClusterFilterSet,
|
Rack,
|
||||||
'table': ClusterTable,
|
'location',
|
||||||
'url': 'virtualization:cluster_list',
|
'rack_count',
|
||||||
}),
|
cumulative=True
|
||||||
('virtualmachine', {
|
).prefetch_related('site'),
|
||||||
'queryset': VirtualMachine.objects.prefetch_related(
|
'filterset': LocationFilterSet,
|
||||||
'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
|
'table': LocationTable,
|
||||||
),
|
'url': 'dcim:location_list',
|
||||||
'filterset': VirtualMachineFilterSet,
|
}),
|
||||||
'table': VirtualMachineTable,
|
('devicetype', {
|
||||||
'url': 'virtualization:virtualmachine_list',
|
'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate(
|
||||||
}),
|
instance_count=count_related(Device, 'device_type')
|
||||||
# IPAM
|
),
|
||||||
('vrf', {
|
'filterset': DeviceTypeFilterSet,
|
||||||
'queryset': VRF.objects.prefetch_related('tenant'),
|
'table': DeviceTypeTable,
|
||||||
'filterset': VRFFilterSet,
|
'url': 'dcim:devicetype_list',
|
||||||
'table': VRFTable,
|
}),
|
||||||
'url': 'ipam:vrf_list',
|
('device', {
|
||||||
}),
|
'queryset': Device.objects.prefetch_related(
|
||||||
('aggregate', {
|
'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6',
|
||||||
'queryset': Aggregate.objects.prefetch_related('rir'),
|
),
|
||||||
'filterset': AggregateFilterSet,
|
'filterset': DeviceFilterSet,
|
||||||
'table': AggregateTable,
|
'table': DeviceTable,
|
||||||
'url': 'ipam:aggregate_list',
|
'url': 'dcim:device_list',
|
||||||
}),
|
}),
|
||||||
('prefix', {
|
('virtualchassis', {
|
||||||
'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'),
|
'queryset': VirtualChassis.objects.prefetch_related('master').annotate(
|
||||||
'filterset': PrefixFilterSet,
|
member_count=count_related(Device, 'virtual_chassis')
|
||||||
'table': PrefixTable,
|
),
|
||||||
'url': 'ipam:prefix_list',
|
'filterset': VirtualChassisFilterSet,
|
||||||
}),
|
'table': VirtualChassisTable,
|
||||||
('ipaddress', {
|
'url': 'dcim:virtualchassis_list',
|
||||||
'queryset': IPAddress.objects.prefetch_related('vrf__tenant', 'tenant'),
|
}),
|
||||||
'filterset': IPAddressFilterSet,
|
('cable', {
|
||||||
'table': IPAddressTable,
|
'queryset': Cable.objects.all(),
|
||||||
'url': 'ipam:ipaddress_list',
|
'filterset': CableFilterSet,
|
||||||
}),
|
'table': CableTable,
|
||||||
('vlan', {
|
'url': 'dcim:cable_list',
|
||||||
'queryset': VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role'),
|
}),
|
||||||
'filterset': VLANFilterSet,
|
('powerfeed', {
|
||||||
'table': VLANTable,
|
'queryset': PowerFeed.objects.all(),
|
||||||
'url': 'ipam:vlan_list',
|
'filterset': PowerFeedFilterSet,
|
||||||
}),
|
'table': PowerFeedTable,
|
||||||
('asn', {
|
'url': 'dcim:powerfeed_list',
|
||||||
'queryset': ASN.objects.prefetch_related('rir', 'tenant'),
|
}),
|
||||||
'filterset': ASNFilterSet,
|
)
|
||||||
'table': ASNTable,
|
)
|
||||||
'url': 'ipam:asn_list',
|
|
||||||
}),
|
IPAM_TYPES = OrderedDict(
|
||||||
# Tenancy
|
(
|
||||||
('tenant', {
|
('vrf', {
|
||||||
'queryset': Tenant.objects.prefetch_related('group'),
|
'queryset': VRF.objects.prefetch_related('tenant'),
|
||||||
'filterset': TenantFilterSet,
|
'filterset': VRFFilterSet,
|
||||||
'table': TenantTable,
|
'table': VRFTable,
|
||||||
'url': 'tenancy:tenant_list',
|
'url': 'ipam:vrf_list',
|
||||||
}),
|
}),
|
||||||
('contact', {
|
('aggregate', {
|
||||||
'queryset': Contact.objects.prefetch_related('group', 'assignments').annotate(assignment_count=count_related(ContactAssignment, 'contact')),
|
'queryset': Aggregate.objects.prefetch_related('rir'),
|
||||||
'filterset': ContactFilterSet,
|
'filterset': AggregateFilterSet,
|
||||||
'table': ContactTable,
|
'table': AggregateTable,
|
||||||
'url': 'tenancy:contact_list',
|
'url': 'ipam:aggregate_list',
|
||||||
}),
|
}),
|
||||||
))
|
('prefix', {
|
||||||
|
'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'),
|
||||||
|
'filterset': PrefixFilterSet,
|
||||||
|
'table': PrefixTable,
|
||||||
|
'url': 'ipam:prefix_list',
|
||||||
|
}),
|
||||||
|
('ipaddress', {
|
||||||
|
'queryset': IPAddress.objects.prefetch_related('vrf__tenant', 'tenant'),
|
||||||
|
'filterset': IPAddressFilterSet,
|
||||||
|
'table': IPAddressTable,
|
||||||
|
'url': 'ipam:ipaddress_list',
|
||||||
|
}),
|
||||||
|
('vlan', {
|
||||||
|
'queryset': VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role'),
|
||||||
|
'filterset': VLANFilterSet,
|
||||||
|
'table': VLANTable,
|
||||||
|
'url': 'ipam:vlan_list',
|
||||||
|
}),
|
||||||
|
('asn', {
|
||||||
|
'queryset': ASN.objects.prefetch_related('rir', 'tenant'),
|
||||||
|
'filterset': ASNFilterSet,
|
||||||
|
'table': ASNTable,
|
||||||
|
'url': 'ipam:asn_list',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
TENANCY_TYPES = OrderedDict(
|
||||||
|
(
|
||||||
|
('tenant', {
|
||||||
|
'queryset': Tenant.objects.prefetch_related('group'),
|
||||||
|
'filterset': TenantFilterSet,
|
||||||
|
'table': TenantTable,
|
||||||
|
'url': 'tenancy:tenant_list',
|
||||||
|
}),
|
||||||
|
('contact', {
|
||||||
|
'queryset': Contact.objects.prefetch_related('group', 'assignments').annotate(
|
||||||
|
assignment_count=count_related(ContactAssignment, 'contact')),
|
||||||
|
'filterset': ContactFilterSet,
|
||||||
|
'table': ContactTable,
|
||||||
|
'url': 'tenancy:contact_list',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
VIRTUALIZATION_TYPES = OrderedDict(
|
||||||
|
(
|
||||||
|
('cluster', {
|
||||||
|
'queryset': Cluster.objects.prefetch_related('type', 'group').annotate(
|
||||||
|
device_count=count_related(Device, 'cluster'),
|
||||||
|
vm_count=count_related(VirtualMachine, 'cluster')
|
||||||
|
),
|
||||||
|
'filterset': ClusterFilterSet,
|
||||||
|
'table': ClusterTable,
|
||||||
|
'url': 'virtualization:cluster_list',
|
||||||
|
}),
|
||||||
|
('virtualmachine', {
|
||||||
|
'queryset': VirtualMachine.objects.prefetch_related(
|
||||||
|
'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
|
||||||
|
),
|
||||||
|
'filterset': VirtualMachineFilterSet,
|
||||||
|
'table': VirtualMachineTable,
|
||||||
|
'url': 'virtualization:virtualmachine_list',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
SEARCH_TYPE_HIERARCHY = OrderedDict(
|
||||||
|
(
|
||||||
|
("Circuits", CIRCUIT_TYPES),
|
||||||
|
("DCIM", DCIM_TYPES),
|
||||||
|
("IPAM", IPAM_TYPES),
|
||||||
|
("Tenancy", TENANCY_TYPES),
|
||||||
|
("Virtualization", VIRTUALIZATION_TYPES),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_search_types() -> Dict[str, Dict]:
|
||||||
|
result = dict()
|
||||||
|
|
||||||
|
for app_types in SEARCH_TYPE_HIERARCHY.values():
|
||||||
|
for name, items in app_types.items():
|
||||||
|
result[name] = items
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
SEARCH_TYPES = build_search_types()
|
||||||
|
@ -1,40 +1,25 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
from netbox.constants import SEARCH_TYPE_HIERARCHY
|
||||||
from utilities.forms import BootstrapMixin
|
from utilities.forms import BootstrapMixin
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
OBJ_TYPE_CHOICES = (
|
|
||||||
('', 'All Objects'),
|
def build_search_choices():
|
||||||
('Circuits', (
|
result = list()
|
||||||
('provider', 'Providers'),
|
result.append(('', 'All Objects'))
|
||||||
('circuit', 'Circuits'),
|
for category, items in SEARCH_TYPE_HIERARCHY.items():
|
||||||
)),
|
subcategories = list()
|
||||||
('DCIM', (
|
for slug, obj in items.items():
|
||||||
('site', 'Sites'),
|
name = obj['queryset'].model._meta.verbose_name_plural
|
||||||
('rack', 'Racks'),
|
name = name[0].upper() + name[1:]
|
||||||
('rackreservation', 'Rack reservations'),
|
subcategories.append((slug, name))
|
||||||
('location', 'Locations'),
|
result.append((category, tuple(subcategories)))
|
||||||
('devicetype', 'Device Types'),
|
|
||||||
('device', 'Devices'),
|
return tuple(result)
|
||||||
('virtualchassis', 'Virtual chassis'),
|
|
||||||
('cable', 'Cables'),
|
|
||||||
('powerfeed', 'Power feeds'),
|
OBJ_TYPE_CHOICES = build_search_choices()
|
||||||
)),
|
|
||||||
('IPAM', (
|
|
||||||
('vrf', 'VRFs'),
|
|
||||||
('aggregate', 'Aggregates'),
|
|
||||||
('prefix', 'Prefixes'),
|
|
||||||
('ipaddress', 'IP Addresses'),
|
|
||||||
('vlan', 'VLANs'),
|
|
||||||
)),
|
|
||||||
('Tenancy', (
|
|
||||||
('tenant', 'Tenants'),
|
|
||||||
)),
|
|
||||||
('Virtualization', (
|
|
||||||
('cluster', 'Clusters'),
|
|
||||||
('virtualmachine', 'Virtual Machines'),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def build_options():
|
def build_options():
|
||||||
|
BIN
netbox/project-static/dist/netbox-dark.css
vendored
BIN
netbox/project-static/dist/netbox-dark.css
vendored
Binary file not shown.
@ -145,9 +145,9 @@ $nav-tabs-link-active-border-color: $gray-800 $gray-800 $nav-tabs-link-active-bg
|
|||||||
$nav-pills-link-active-color: $component-active-color;
|
$nav-pills-link-active-color: $component-active-color;
|
||||||
$nav-pills-link-active-bg: $component-active-bg;
|
$nav-pills-link-active-bg: $component-active-bg;
|
||||||
|
|
||||||
$navbar-light-color: $navbar-dark-color;
|
$navbar-light-color: $darker;
|
||||||
$navbar-light-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-light-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>");
|
|
||||||
$navbar-light-toggler-border-color: $gray-700;
|
$navbar-light-toggler-border-color: $gray-700;
|
||||||
|
$navbar-light-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-light-toggler-border-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>");
|
||||||
|
|
||||||
// Dropdowns
|
// Dropdowns
|
||||||
$dropdown-color: $body-color;
|
$dropdown-color: $body-color;
|
||||||
|
@ -37,9 +37,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Serial Number</th>
|
<th scope="row">Serial Number</th>
|
||||||
<td>
|
<td id="serial_number" class="text-monospace"></td>
|
||||||
<code id="serial_number"></code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">OS Version</th>
|
<th scope="row">OS Version</th>
|
||||||
|
@ -8,6 +8,22 @@
|
|||||||
<a href="{{ termination.device.get_absolute_url }}">{{ termination.device }}</a>
|
<a href="{{ termination.device.get_absolute_url }}">{{ termination.device }}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if termination.device.site %}
|
||||||
|
<tr>
|
||||||
|
<td>Site</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ termination.device.site.get_absolute_url }}">{{ termination.device.site }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if termination.device.rack %}
|
||||||
|
<tr>
|
||||||
|
<td>Rack</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ termination.device.rack.get_absolute_url }}">{{ termination.device.rack }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Type</td>
|
<td>Type</td>
|
||||||
<td>
|
<td>
|
||||||
|
62
netbox/templates/dcim/inc/nonracked_devices.html
Normal file
62
netbox/templates/dcim/inc/nonracked_devices.html
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">
|
||||||
|
Non-Racked Devices
|
||||||
|
</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if nonracked_devices %}
|
||||||
|
<table class="table table-hover">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th colspan="2">Parent Device</th>
|
||||||
|
</tr>
|
||||||
|
{% for device in nonracked_devices %}
|
||||||
|
<tr{% if device.device_type.u_height %} class="warning"{% endif %}>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'dcim:device' pk=device.pk %}">{{ device }}</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ device.device_role }}</td>
|
||||||
|
<td>{{ device.device_type }}</td>
|
||||||
|
{% if device.parent_bay %}
|
||||||
|
<td><a href="{{ device.parent_bay.device.get_absolute_url }}">{{ device.parent_bay.device }}</a></td>
|
||||||
|
<td>{{ device.parent_bay }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td colspan="2" class="text-muted">—</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-muted">
|
||||||
|
None
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if perms.dcim.add_device %}
|
||||||
|
{% if object|meta:'verbose_name' == 'rack' %}
|
||||||
|
<div class="card-footer text-end noprint">
|
||||||
|
<a href="{% url 'dcim:device_add' %}?site={{ object.site.pk }}&rack={{ object.pk }}" class="btn btn-primary btn-sm">
|
||||||
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
||||||
|
Add a Non-Racked Device
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% elif object|meta:'verbose_name' == 'site' %}
|
||||||
|
<div class="card-footer text-end noprint">
|
||||||
|
<a href="{% url 'dcim:device_add' %}?site={{ object.pk }}" class="btn btn-primary btn-sm">
|
||||||
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
||||||
|
Add a Non-Racked Device
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% elif object|meta:'verbose_name' == 'location' %}
|
||||||
|
<div class="card-footer text-end noprint">
|
||||||
|
<a href="{% url 'dcim:device_add' %}?site={{ object.site.pk }}&location={{ object.pk }}" class="btn btn-primary btn-sm">
|
||||||
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
||||||
|
Add a Non-Racked Device
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
@ -90,6 +90,7 @@
|
|||||||
<div class="col col-md-6">
|
<div class="col col-md-6">
|
||||||
{% include 'inc/panels/custom_fields.html' %}
|
{% include 'inc/panels/custom_fields.html' %}
|
||||||
{% include 'inc/panels/contacts.html' %}
|
{% include 'inc/panels/contacts.html' %}
|
||||||
|
{% include 'dcim/inc/nonracked_devices.html' %}
|
||||||
{% include 'inc/panels/image_attachments.html' %}
|
{% include 'inc/panels/image_attachments.html' %}
|
||||||
{% plugin_right_page object %}
|
{% plugin_right_page object %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -286,50 +286,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
{% include 'dcim/inc/nonracked_devices.html' %}
|
||||||
<h5 class="card-header">
|
|
||||||
Non-Racked Devices
|
|
||||||
</h5>
|
|
||||||
<div class="card-body">
|
|
||||||
{% if nonracked_devices %}
|
|
||||||
<table class="table table-hover">
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Role</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th colspan="2">Parent Device</th>
|
|
||||||
</tr>
|
|
||||||
{% for device in nonracked_devices %}
|
|
||||||
<tr{% if device.device_type.u_height %} class="warning"{% endif %}>
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'dcim:device' pk=device.pk %}">{{ device }}</a>
|
|
||||||
</td>
|
|
||||||
<td>{{ device.device_role }}</td>
|
|
||||||
<td>{{ device.device_type }}</td>
|
|
||||||
{% if device.parent_bay %}
|
|
||||||
<td><a href="{{ device.parent_bay.device.get_absolute_url }}">{{ device.parent_bay.device }}</a></td>
|
|
||||||
<td>{{ device.parent_bay }}</td>
|
|
||||||
{% else %}
|
|
||||||
<td colspan="2" class="text-muted">—</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
<div class="text-muted">
|
|
||||||
None
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if perms.dcim.add_device %}
|
|
||||||
<div class="card-footer text-end noprint">
|
|
||||||
<a href="{% url 'dcim:device_add' %}?site={{ object.site.pk }}&rack={{ object.pk }}" class="btn btn-primary btn-sm">
|
|
||||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
||||||
Add a Non-Racked Device
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% include 'inc/panels/contacts.html' %}
|
{% include 'inc/panels/contacts.html' %}
|
||||||
{% plugin_right_page object %}
|
{% plugin_right_page object %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -225,6 +225,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% include 'dcim/inc/nonracked_devices.html' %}
|
||||||
{% include 'inc/panels/contacts.html' %}
|
{% include 'inc/panels/contacts.html' %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h5 class="card-header">Locations</h5>
|
<h5 class="card-header">Locations</h5>
|
||||||
|
@ -10,6 +10,7 @@ __all__ = (
|
|||||||
'ContactAssignmentFilterSet',
|
'ContactAssignmentFilterSet',
|
||||||
'ContactFilterSet',
|
'ContactFilterSet',
|
||||||
'ContactGroupFilterSet',
|
'ContactGroupFilterSet',
|
||||||
|
'ContactModelFilterSet',
|
||||||
'ContactRoleFilterSet',
|
'ContactRoleFilterSet',
|
||||||
'TenancyFilterSet',
|
'TenancyFilterSet',
|
||||||
'TenantFilterSet',
|
'TenantFilterSet',
|
||||||
@ -17,86 +18,6 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Tenancy
|
|
||||||
#
|
|
||||||
|
|
||||||
class TenantGroupFilterSet(OrganizationalModelFilterSet):
|
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
queryset=TenantGroup.objects.all(),
|
|
||||||
label='Tenant group (ID)',
|
|
||||||
)
|
|
||||||
parent = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
field_name='parent__slug',
|
|
||||||
queryset=TenantGroup.objects.all(),
|
|
||||||
to_field_name='slug',
|
|
||||||
label='Tenant group (slug)',
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = TenantGroup
|
|
||||||
fields = ['id', 'name', 'slug', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class TenantFilterSet(NetBoxModelFilterSet):
|
|
||||||
group_id = TreeNodeMultipleChoiceFilter(
|
|
||||||
queryset=TenantGroup.objects.all(),
|
|
||||||
field_name='group',
|
|
||||||
lookup_expr='in',
|
|
||||||
label='Tenant group (ID)',
|
|
||||||
)
|
|
||||||
group = TreeNodeMultipleChoiceFilter(
|
|
||||||
queryset=TenantGroup.objects.all(),
|
|
||||||
field_name='group',
|
|
||||||
lookup_expr='in',
|
|
||||||
to_field_name='slug',
|
|
||||||
label='Tenant group (slug)',
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Tenant
|
|
||||||
fields = ['id', 'name', 'slug', 'description']
|
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
|
||||||
if not value.strip():
|
|
||||||
return queryset
|
|
||||||
return queryset.filter(
|
|
||||||
Q(name__icontains=value) |
|
|
||||||
Q(slug__icontains=value) |
|
|
||||||
Q(description__icontains=value) |
|
|
||||||
Q(comments__icontains=value)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TenancyFilterSet(django_filters.FilterSet):
|
|
||||||
"""
|
|
||||||
An inheritable FilterSet for models which support Tenant assignment.
|
|
||||||
"""
|
|
||||||
tenant_group_id = TreeNodeMultipleChoiceFilter(
|
|
||||||
queryset=TenantGroup.objects.all(),
|
|
||||||
field_name='tenant__group',
|
|
||||||
lookup_expr='in',
|
|
||||||
label='Tenant Group (ID)',
|
|
||||||
)
|
|
||||||
tenant_group = TreeNodeMultipleChoiceFilter(
|
|
||||||
queryset=TenantGroup.objects.all(),
|
|
||||||
field_name='tenant__group',
|
|
||||||
to_field_name='slug',
|
|
||||||
lookup_expr='in',
|
|
||||||
label='Tenant Group (slug)',
|
|
||||||
)
|
|
||||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
queryset=Tenant.objects.all(),
|
|
||||||
label='Tenant (ID)',
|
|
||||||
)
|
|
||||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
queryset=Tenant.objects.all(),
|
|
||||||
field_name='tenant__slug',
|
|
||||||
to_field_name='slug',
|
|
||||||
label='Tenant (slug)',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Contacts
|
# Contacts
|
||||||
#
|
#
|
||||||
@ -177,3 +98,96 @@ class ContactAssignmentFilterSet(ChangeLoggedModelFilterSet):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ContactAssignment
|
model = ContactAssignment
|
||||||
fields = ['id', 'content_type_id', 'object_id', 'priority']
|
fields = ['id', 'content_type_id', 'object_id', 'priority']
|
||||||
|
|
||||||
|
|
||||||
|
class ContactModelFilterSet(django_filters.FilterSet):
|
||||||
|
contact = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='contacts__contact',
|
||||||
|
queryset=Contact.objects.all(),
|
||||||
|
label='Contact',
|
||||||
|
)
|
||||||
|
contact_role = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='contacts__role',
|
||||||
|
queryset=ContactRole.objects.all(),
|
||||||
|
label='Contact Role'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Tenancy
|
||||||
|
#
|
||||||
|
|
||||||
|
class TenantGroupFilterSet(OrganizationalModelFilterSet):
|
||||||
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
label='Tenant group (ID)',
|
||||||
|
)
|
||||||
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='parent__slug',
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Tenant group (slug)',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = TenantGroup
|
||||||
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
|
||||||
|
group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
field_name='group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Tenant group (ID)',
|
||||||
|
)
|
||||||
|
group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
field_name='group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Tenant group (slug)',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Tenant
|
||||||
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
def search(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(
|
||||||
|
Q(name__icontains=value) |
|
||||||
|
Q(slug__icontains=value) |
|
||||||
|
Q(description__icontains=value) |
|
||||||
|
Q(comments__icontains=value)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TenancyFilterSet(django_filters.FilterSet):
|
||||||
|
"""
|
||||||
|
An inheritable FilterSet for models which support Tenant assignment.
|
||||||
|
"""
|
||||||
|
tenant_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
field_name='tenant__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Tenant Group (ID)',
|
||||||
|
)
|
||||||
|
tenant_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
field_name='tenant__group',
|
||||||
|
to_field_name='slug',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Tenant Group (slug)',
|
||||||
|
)
|
||||||
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
label='Tenant (ID)',
|
||||||
|
)
|
||||||
|
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
field_name='tenant__slug',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Tenant (slug)',
|
||||||
|
)
|
||||||
|
@ -2,6 +2,7 @@ from django.utils.translation import gettext as _
|
|||||||
|
|
||||||
from netbox.forms import NetBoxModelFilterSetForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.models import *
|
from tenancy.models import *
|
||||||
|
from tenancy.forms import ContactModelFilterForm
|
||||||
from utilities.forms import DynamicModelMultipleChoiceField, TagFilterField
|
from utilities.forms import DynamicModelMultipleChoiceField, TagFilterField
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -27,8 +28,12 @@ class TenantGroupFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class TenantFilterForm(NetBoxModelFilterSetForm):
|
class TenantFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Tenant
|
model = Tenant
|
||||||
|
fieldsets = (
|
||||||
|
(None, ('q', 'tag', 'group_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role'))
|
||||||
|
)
|
||||||
group_id = DynamicModelMultipleChoiceField(
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import *
|
||||||
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'ContactModelFilterForm',
|
||||||
'TenancyForm',
|
'TenancyForm',
|
||||||
'TenancyFilterForm',
|
'TenancyFilterForm',
|
||||||
)
|
)
|
||||||
@ -44,3 +45,16 @@ class TenancyFilterForm(forms.Form):
|
|||||||
},
|
},
|
||||||
label=_('Tenant')
|
label=_('Tenant')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContactModelFilterForm(forms.Form):
|
||||||
|
contact = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Contact.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Contact')
|
||||||
|
)
|
||||||
|
contact_role = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=ContactRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Contact Role')
|
||||||
|
)
|
||||||
|
@ -162,3 +162,6 @@ class ContactAssignment(WebhooksMixin, ChangeLoggedModel):
|
|||||||
if self.priority:
|
if self.priority:
|
||||||
return f"{self.contact} ({self.get_priority_display()})"
|
return f"{self.contact} ({self.get_priority_display()})"
|
||||||
return str(self.contact)
|
return str(self.contact)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('tenancy:contact', args=[self.contact.pk])
|
||||||
|
@ -38,11 +38,17 @@ class TenantTable(NetBoxTable):
|
|||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='tenancy:tenant_list'
|
url_name='tenancy:tenant_list'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Tenant
|
model = Tenant
|
||||||
fields = ('pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'created', 'last_updated',)
|
fields = (
|
||||||
|
'pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'contacts', 'tags', 'created',
|
||||||
|
'last_updated',
|
||||||
|
)
|
||||||
default_columns = ('pk', 'name', 'group', 'description')
|
default_columns = ('pk', 'name', 'group', 'description')
|
||||||
|
@ -5,7 +5,7 @@ from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
|||||||
from extras.filtersets import LocalConfigContextFilterSet
|
from extras.filtersets import LocalConfigContextFilterSet
|
||||||
from ipam.models import VRF
|
from ipam.models import VRF
|
||||||
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
|
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
|
||||||
from tenancy.filtersets import TenancyFilterSet
|
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
|
||||||
from utilities.filters import MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
|
from utilities.filters import MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
||||||
@ -26,14 +26,14 @@ class ClusterTypeFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupFilterSet(OrganizationalModelFilterSet):
|
class ClusterGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ClusterGroup
|
model = ClusterGroup
|
||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
@ -104,7 +104,12 @@ class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineFilterSet(NetBoxModelFilterSet, TenancyFilterSet, LocalConfigContextFilterSet):
|
class VirtualMachineFilterSet(
|
||||||
|
NetBoxModelFilterSet,
|
||||||
|
TenancyFilterSet,
|
||||||
|
ContactModelFilterSet,
|
||||||
|
LocalConfigContextFilterSet
|
||||||
|
):
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=VirtualMachineStatusChoices,
|
choices=VirtualMachineStatusChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
|
@ -5,7 +5,7 @@ from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
|||||||
from extras.forms import LocalConfigContextFilterForm
|
from extras.forms import LocalConfigContextFilterForm
|
||||||
from ipam.models import VRF
|
from ipam.models import VRF
|
||||||
from netbox.forms import NetBoxModelFilterSetForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
||||||
)
|
)
|
||||||
@ -26,18 +26,19 @@ class ClusterTypeFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupFilterForm(NetBoxModelFilterSetForm):
|
class ClusterGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = ClusterGroup
|
model = ClusterGroup
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ClusterFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
class ClusterFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Cluster
|
model = Cluster
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Attributes', ('group_id', 'type_id')),
|
('Attributes', ('group_id', 'type_id')),
|
||||||
('Location', ('region_id', 'site_group_id', 'site_id')),
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
type_id = DynamicModelMultipleChoiceField(
|
type_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
@ -73,7 +74,12 @@ class ClusterFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
|
class VirtualMachineFilterForm(
|
||||||
|
LocalConfigContextFilterForm,
|
||||||
|
TenancyFilterForm,
|
||||||
|
ContactModelFilterForm,
|
||||||
|
NetBoxModelFilterSetForm
|
||||||
|
):
|
||||||
model = VirtualMachine
|
model = VirtualMachine
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
@ -81,6 +87,7 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
|
|||||||
('Location', ('region_id', 'site_group_id', 'site_id')),
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
('Attriubtes', ('status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'local_context_data')),
|
('Attriubtes', ('status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'local_context_data')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Contacts', ('contact', 'contact_role')),
|
||||||
)
|
)
|
||||||
cluster_group_id = DynamicModelMultipleChoiceField(
|
cluster_group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
|
@ -36,6 +36,9 @@ class ClusterGroupTable(NetBoxTable):
|
|||||||
cluster_count = tables.Column(
|
cluster_count = tables.Column(
|
||||||
verbose_name='Clusters'
|
verbose_name='Clusters'
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='virtualization:clustergroup_list'
|
url_name='virtualization:clustergroup_list'
|
||||||
)
|
)
|
||||||
@ -43,7 +46,8 @@ class ClusterGroupTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = ClusterGroup
|
model = ClusterGroup
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'tags', 'created', 'last_updated', 'actions',
|
'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'contacts', 'tags', 'created', 'last_updated',
|
||||||
|
'actions',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'cluster_count', 'description')
|
default_columns = ('pk', 'name', 'cluster_count', 'description')
|
||||||
|
|
||||||
@ -75,6 +79,9 @@ class ClusterTable(NetBoxTable):
|
|||||||
verbose_name='VMs'
|
verbose_name='VMs'
|
||||||
)
|
)
|
||||||
comments = columns.MarkdownColumn()
|
comments = columns.MarkdownColumn()
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='virtualization:cluster_list'
|
url_name='virtualization:cluster_list'
|
||||||
)
|
)
|
||||||
@ -82,7 +89,7 @@ class ClusterTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Cluster
|
model = Cluster
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'tags',
|
'pk', 'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'contacts',
|
||||||
'created', 'last_updated',
|
'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
|
default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
|
||||||
|
@ -78,6 +78,9 @@ class VMInterfaceTable(BaseInterfaceTable):
|
|||||||
vrf = tables.Column(
|
vrf = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
contacts = tables.ManyToManyColumn(
|
||||||
|
linkify_item=True
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='virtualization:vminterface_list'
|
url_name='virtualization:vminterface_list'
|
||||||
)
|
)
|
||||||
@ -86,7 +89,8 @@ class VMInterfaceTable(BaseInterfaceTable):
|
|||||||
model = VMInterface
|
model = VMInterface
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'tags',
|
'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'tags',
|
||||||
'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'created', 'last_updated',
|
'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'contacts', 'created',
|
||||||
|
'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description')
|
default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user