mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Add 'available_on' VLAN filters for devices & VMs
This commit is contained in:
@ -3077,20 +3077,12 @@ class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm):
|
|||||||
untagged_vlan = DynamicModelChoiceField(
|
untagged_vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Untagged VLAN',
|
label='Untagged VLAN'
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Tagged VLANs',
|
label='Tagged VLANs'
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -3124,9 +3116,9 @@ class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm):
|
|||||||
self.fields['parent'].widget.add_query_param('device_id', device.pk)
|
self.fields['parent'].widget.add_query_param('device_id', device.pk)
|
||||||
self.fields['lag'].widget.add_query_param('device_id', device.pk)
|
self.fields['lag'].widget.add_query_param('device_id', device.pk)
|
||||||
|
|
||||||
# Add current site to VLANs query params
|
# Limit VLAN choices by device
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('site_id', device.site.pk)
|
self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device.pk)
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('site_id', device.site.pk)
|
self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device.pk)
|
||||||
|
|
||||||
|
|
||||||
class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
||||||
@ -3177,19 +3169,11 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
|||||||
)
|
)
|
||||||
untagged_vlan = DynamicModelChoiceField(
|
untagged_vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
field_order = (
|
field_order = (
|
||||||
'device', 'name_pattern', 'label_pattern', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
|
'device', 'name_pattern', 'label_pattern', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
|
||||||
@ -3199,12 +3183,10 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Add current site to VLANs query params
|
# Limit VLAN choices by device
|
||||||
device = Device.objects.get(
|
device_id = self.initial.get('device') or self.data.get('device')
|
||||||
pk=self.initial.get('device') or self.data.get('device')
|
self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device_id)
|
||||||
)
|
self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device_id)
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('site_id', device.site.pk)
|
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('site_id', device.site.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceBulkCreateForm(
|
class InterfaceBulkCreateForm(
|
||||||
@ -3264,19 +3246,11 @@ class InterfaceBulkEditForm(
|
|||||||
)
|
)
|
||||||
untagged_vlan = DynamicModelChoiceField(
|
untagged_vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -3293,9 +3267,9 @@ class InterfaceBulkEditForm(
|
|||||||
self.fields['parent'].widget.add_query_param('device_id', device.pk)
|
self.fields['parent'].widget.add_query_param('device_id', device.pk)
|
||||||
self.fields['lag'].widget.add_query_param('device_id', device.pk)
|
self.fields['lag'].widget.add_query_param('device_id', device.pk)
|
||||||
|
|
||||||
# Add current site to VLANs query params
|
# Limit VLAN choices by device
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('site_id', device.site.pk)
|
self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device.pk)
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('site_id', device.site.pk)
|
self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device.pk)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# See #4523
|
# See #4523
|
||||||
|
@ -635,6 +635,14 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
|
|||||||
choices=VLANStatusChoices,
|
choices=VLANStatusChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
|
available_on_device = django_filters.ModelChoiceFilter(
|
||||||
|
queryset=Device.objects.all(),
|
||||||
|
method='get_for_device'
|
||||||
|
)
|
||||||
|
available_on_virtualmachine = django_filters.ModelChoiceFilter(
|
||||||
|
queryset=VirtualMachine.objects.all(),
|
||||||
|
method='get_for_virtualmachine'
|
||||||
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -651,6 +659,12 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
|
|||||||
pass
|
pass
|
||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
|
|
||||||
|
def get_for_device(self, queryset, name, value):
|
||||||
|
return queryset.get_for_device(value)
|
||||||
|
|
||||||
|
def get_for_virtualmachine(self, queryset, name, value):
|
||||||
|
return queryset.get_for_virtualmachine(value)
|
||||||
|
|
||||||
|
|
||||||
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
|
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
|
@ -9,6 +9,7 @@ from dcim.models import Interface
|
|||||||
from extras.utils import extras_features
|
from extras.utils import extras_features
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import *
|
from ipam.constants import *
|
||||||
|
from ipam.querysets import VLANQuerySet
|
||||||
from netbox.models import OrganizationalModel, PrimaryModel
|
from netbox.models import OrganizationalModel, PrimaryModel
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
from virtualization.models import VMInterface
|
from virtualization.models import VMInterface
|
||||||
@ -156,7 +157,7 @@ class VLAN(PrimaryModel):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = RestrictedQuerySet.as_manager()
|
objects = VLANQuerySet.as_manager()
|
||||||
|
|
||||||
csv_headers = ['site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
csv_headers = ['site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
||||||
clone_fields = [
|
clone_fields = [
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
|
|
||||||
|
|
||||||
@ -20,3 +23,84 @@ class PrefixQuerySet(RestrictedQuerySet):
|
|||||||
'AND COALESCE(U1."vrf_id", 0) = COALESCE("ipam_prefix"."vrf_id", 0))',
|
'AND COALESCE(U1."vrf_id", 0) = COALESCE("ipam_prefix"."vrf_id", 0))',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VLANQuerySet(RestrictedQuerySet):
|
||||||
|
|
||||||
|
def get_for_device(self, device):
|
||||||
|
"""
|
||||||
|
Return all VLANs available to the specified Device.
|
||||||
|
"""
|
||||||
|
from .models import VLANGroup
|
||||||
|
|
||||||
|
# Find all relevant VLANGroups
|
||||||
|
q = Q()
|
||||||
|
if device.site.region:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
|
||||||
|
scope_id__in=device.site.region.get_ancestors(include_self=True)
|
||||||
|
)
|
||||||
|
if device.site.group:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
|
||||||
|
scope_id__in=device.site.group.get_ancestors(include_self=True)
|
||||||
|
)
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
|
||||||
|
scope_id=device.site_id
|
||||||
|
)
|
||||||
|
if device.location:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'location'),
|
||||||
|
scope_id__in=device.location.get_ancestors(include_self=True)
|
||||||
|
)
|
||||||
|
if device.rack:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'rack'),
|
||||||
|
scope_id=device.rack_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.filter(
|
||||||
|
Q(group__in=VLANGroup.objects.filter(q)) |
|
||||||
|
Q(site=device.site) |
|
||||||
|
Q(group__isnull=True, site__isnull=True) # Global VLANs
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_for_virtualmachine(self, vm):
|
||||||
|
"""
|
||||||
|
Return all VLANs available to the specified VirtualMachine.
|
||||||
|
"""
|
||||||
|
from .models import VLANGroup
|
||||||
|
|
||||||
|
# Find all relevant VLANGroups
|
||||||
|
q = Q()
|
||||||
|
if vm.cluster.site:
|
||||||
|
if vm.cluster.site.region:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),
|
||||||
|
scope_id__in=vm.cluster.site.region.get_ancestors(include_self=True)
|
||||||
|
)
|
||||||
|
if vm.cluster.site.group:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'),
|
||||||
|
scope_id__in=vm.cluster.site.group.get_ancestors(include_self=True)
|
||||||
|
)
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'),
|
||||||
|
scope_id=vm.cluster.site_id
|
||||||
|
)
|
||||||
|
if vm.cluster.group:
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'),
|
||||||
|
scope_id=vm.cluster.group_id
|
||||||
|
)
|
||||||
|
q |= Q(
|
||||||
|
scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'),
|
||||||
|
scope_id=vm.cluster_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.filter(
|
||||||
|
Q(group__in=VLANGroup.objects.filter(q)) |
|
||||||
|
Q(site=vm.cluster.site) |
|
||||||
|
Q(group__isnull=True, site__isnull=True) # Global VLANs
|
||||||
|
)
|
||||||
|
@ -606,20 +606,12 @@ class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm)
|
|||||||
untagged_vlan = DynamicModelChoiceField(
|
untagged_vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Untagged VLAN',
|
label='Untagged VLAN'
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Tagged VLANs',
|
label='Tagged VLANs'
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -646,15 +638,10 @@ class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm)
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
virtual_machine = VirtualMachine.objects.get(
|
# Limit VLAN choices by virtual machine
|
||||||
pk=self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
vm_id = self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
||||||
)
|
self.fields['untagged_vlan'].widget.add_query_param('available_on_vm', vm_id)
|
||||||
|
self.fields['tagged_vlans'].widget.add_query_param('available_on_vm', vm_id)
|
||||||
# Add current site to VLANs query params
|
|
||||||
site = virtual_machine.site
|
|
||||||
if site:
|
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('site_id', site.pk)
|
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('site_id', site.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceCreateForm(BootstrapMixin, InterfaceCommonForm):
|
class VMInterfaceCreateForm(BootstrapMixin, InterfaceCommonForm):
|
||||||
@ -689,19 +676,11 @@ class VMInterfaceCreateForm(BootstrapMixin, InterfaceCommonForm):
|
|||||||
)
|
)
|
||||||
untagged_vlan = DynamicModelChoiceField(
|
untagged_vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -711,15 +690,10 @@ class VMInterfaceCreateForm(BootstrapMixin, InterfaceCommonForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
virtual_machine = VirtualMachine.objects.get(
|
# Limit VLAN choices by virtual machine
|
||||||
pk=self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
vm_id = self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
||||||
)
|
self.fields['untagged_vlan'].widget.add_query_param('available_on_vm', vm_id)
|
||||||
|
self.fields['tagged_vlans'].widget.add_query_param('available_on_vm', vm_id)
|
||||||
# Add current site to VLANs query params
|
|
||||||
site = virtual_machine.site
|
|
||||||
if site:
|
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('site_id', site.pk)
|
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('site_id', site.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceCSVForm(CSVModelForm):
|
class VMInterfaceCSVForm(CSVModelForm):
|
||||||
@ -777,19 +751,11 @@ class VMInterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
|||||||
)
|
)
|
||||||
untagged_vlan = DynamicModelChoiceField(
|
untagged_vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False
|
||||||
brief_mode=False,
|
|
||||||
query_params={
|
|
||||||
'site_id': 'null',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -800,15 +766,10 @@ class VMInterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Limit available VLANs based on the parent VirtualMachine
|
# Limit VLAN choices by virtual machine
|
||||||
if 'virtual_machine' in self.initial:
|
vm_id = self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
||||||
parent_obj = VirtualMachine.objects.filter(pk=self.initial['virtual_machine']).first()
|
self.fields['untagged_vlan'].widget.add_query_param('available_on_vm', vm_id)
|
||||||
|
self.fields['tagged_vlans'].widget.add_query_param('available_on_vm', vm_id)
|
||||||
site = getattr(parent_obj.cluster, 'site', None)
|
|
||||||
if site is not None:
|
|
||||||
# Add current site to VLANs query params
|
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('site_id', site.pk)
|
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('site_id', site.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceBulkRenameForm(BulkRenameForm):
|
class VMInterfaceBulkRenameForm(BulkRenameForm):
|
||||||
|
Reference in New Issue
Block a user