Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #3589: Interface VLAN filtering #3814

Merged
merged 11 commits into from
Jan 6, 2020
1 change: 1 addition & 0 deletions docs/release-notes/version-2.6.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
## Bug Fixes

* [#3106](https://github.com/netbox-community/netbox/issues/3106) - Restrict queryset of chained fields when form validation fails
* [#3589](https://github.com/netbox-community/netbox/issues/3589) - Limit the list of available VLANs to those valid and perform input validation on the tagged VLANs
* [#3695](https://github.com/netbox-community/netbox/issues/3695) - Include A/Z termination sites for circuits in global search
* [#3712](https://github.com/netbox-community/netbox/issues/3712) - Scrolling to target (hash) did not account for the header size
* [#3780](https://github.com/netbox-community/netbox/issues/3780) - Fix AttributeError exception in API docs
Expand Down
125 changes: 38 additions & 87 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ def clean(self):
elif self.cleaned_data['mode'] == IFACE_MODE_TAGGED_ALL:
self.cleaned_data['tagged_vlans'] = []

# Validate tagged VLANs; must be a global VLAN or in the same site
else:
hSaria marked this conversation as resolved.
Show resolved Hide resolved
valid_sites = [None, self.cleaned_data['device'].site]
invalid_vlans = [str(v) for v in tagged_vlans if v.site not in valid_sites]

if invalid_vlans:
raise forms.ValidationError({
'tagged_vlans': "The tagged VLANs ({}) must belong to the same site as the interface's parent "
"device/VM, or they must be global".format(', '.join(invalid_vlans))
})


class BulkRenameForm(forms.Form):
"""
Expand Down Expand Up @@ -2176,6 +2187,9 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
widget=APISelect(
api_url="/api/ipam/vlans/",
display_field='display_name',
additional_query_params={
'site_id': 'null'
},
full=True
)
)
Expand All @@ -2185,6 +2199,9 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
widget=APISelectMultiple(
api_url="/api/ipam/vlans/",
display_field='display_name',
additional_query_params={
'site_id': 'null'
},
full=True
)
)
Expand Down Expand Up @@ -2227,35 +2244,9 @@ def __init__(self, *args, **kwargs):
device__in=[self.instance.device, self.instance.device.get_vc_master()], type=IFACE_TYPE_LAG
)

# Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site
vlan_choices = []
global_vlans = VLAN.objects.filter(site=None, group=None)
vlan_choices.append(
('Global', [(vlan.pk, vlan) for vlan in global_vlans])
)
for group in VLANGroup.objects.filter(site=None):
global_group_vlans = VLAN.objects.filter(group=group)
vlan_choices.append(
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
)

site = getattr(self.instance.parent, 'site', None)
if site is not None:

# Add non-grouped site VLANs
site_vlans = VLAN.objects.filter(site=site, group=None)
vlan_choices.append((site.name, [(vlan.pk, vlan) for vlan in site_vlans]))

# Add grouped site VLANs
for group in VLANGroup.objects.filter(site=site):
site_group_vlans = VLAN.objects.filter(group=group)
vlan_choices.append((
'{} / {}'.format(group.site.name, group.name),
[(vlan.pk, vlan) for vlan in site_group_vlans]
))

self.fields['untagged_vlan'].choices = [(None, '---------')] + vlan_choices
self.fields['tagged_vlans'].choices = vlan_choices
# Add the current site to the list of filtered VLANs
self.fields['untagged_vlan'].widget.attrs['1-data-additional-query-param-site_id'] = self.instance.device.site.pk
hSaria marked this conversation as resolved.
Show resolved Hide resolved
self.fields['tagged_vlans'].widget.attrs['1-data-additional-query-param-site_id'] = self.instance.device.site.pk
hSaria marked this conversation as resolved.
Show resolved Hide resolved


class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form):
Expand Down Expand Up @@ -2308,6 +2299,9 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form):
widget=APISelect(
api_url="/api/ipam/vlans/",
display_field='display_name',
additional_query_params={
'site_id': 'null'
},
full=True
)
)
Expand All @@ -2317,6 +2311,9 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form):
widget=APISelectMultiple(
api_url="/api/ipam/vlans/",
display_field='display_name',
additional_query_params={
'site_id': 'null'
},
full=True
)
)
Expand All @@ -2337,35 +2334,9 @@ def __init__(self, *args, **kwargs):
else:
self.fields['lag'].queryset = Interface.objects.none()

# Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site
vlan_choices = []
global_vlans = VLAN.objects.filter(site=None, group=None)
vlan_choices.append(
('Global', [(vlan.pk, vlan) for vlan in global_vlans])
)
for group in VLANGroup.objects.filter(site=None):
global_group_vlans = VLAN.objects.filter(group=group)
vlan_choices.append(
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
)

site = getattr(self.parent, 'site', None)
if site is not None:

# Add non-grouped site VLANs
site_vlans = VLAN.objects.filter(site=site, group=None)
vlan_choices.append((site.name, [(vlan.pk, vlan) for vlan in site_vlans]))

# Add grouped site VLANs
for group in VLANGroup.objects.filter(site=site):
site_group_vlans = VLAN.objects.filter(group=group)
vlan_choices.append((
'{} / {}'.format(group.site.name, group.name),
[(vlan.pk, vlan) for vlan in site_group_vlans]
))

self.fields['untagged_vlan'].choices = [(None, '---------')] + vlan_choices
self.fields['tagged_vlans'].choices = vlan_choices
# Add the current site to the list of filtered VLANs
self.fields['untagged_vlan'].widget.attrs['1-data-additional-query-param-site_id'] = self.parent.site.pk
hSaria marked this conversation as resolved.
Show resolved Hide resolved
self.fields['tagged_vlans'].widget.attrs['1-data-additional-query-param-site_id'] = self.parent.site.pk
hSaria marked this conversation as resolved.
Show resolved Hide resolved


class InterfaceBulkEditForm(InterfaceCommonForm, BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
Expand Down Expand Up @@ -2418,6 +2389,9 @@ class InterfaceBulkEditForm(InterfaceCommonForm, BootstrapMixin, AddRemoveTagsFo
widget=APISelect(
api_url="/api/ipam/vlans/",
display_field='display_name',
additional_query_params={
'site_id': 'null'
},
full=True
)
)
Expand All @@ -2427,6 +2401,9 @@ class InterfaceBulkEditForm(InterfaceCommonForm, BootstrapMixin, AddRemoveTagsFo
widget=APISelectMultiple(
api_url="/api/ipam/vlans/",
display_field='display_name',
additional_query_params={
'site_id': 'null'
},
full=True
)
)
Expand All @@ -2449,35 +2426,9 @@ def __init__(self, *args, **kwargs):
else:
self.fields['lag'].choices = []

# Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site
vlan_choices = []
global_vlans = VLAN.objects.filter(site=None, group=None)
vlan_choices.append(
('Global', [(vlan.pk, vlan) for vlan in global_vlans])
)
for group in VLANGroup.objects.filter(site=None):
global_group_vlans = VLAN.objects.filter(group=group)
vlan_choices.append(
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
)
if self.parent_obj is not None:
site = getattr(self.parent_obj, 'site', None)
if site is not None:

# Add non-grouped site VLANs
site_vlans = VLAN.objects.filter(site=site, group=None)
vlan_choices.append((site.name, [(vlan.pk, vlan) for vlan in site_vlans]))

# Add grouped site VLANs
for group in VLANGroup.objects.filter(site=site):
site_group_vlans = VLAN.objects.filter(group=group)
vlan_choices.append((
'{} / {}'.format(group.site.name, group.name),
[(vlan.pk, vlan) for vlan in site_group_vlans]
))

self.fields['untagged_vlan'].choices = [(None, '---------')] + vlan_choices
self.fields['tagged_vlans'].choices = vlan_choices
# Add the current site to the list of filtered VLANs
self.fields['untagged_vlan'].widget.attrs['1-data-additional-query-param-site_id'] = self.parent_obj.site.pk
hSaria marked this conversation as resolved.
Show resolved Hide resolved
self.fields['tagged_vlans'].widget.attrs['1-data-additional-query-param-site_id'] = self.parent_obj.site.pk
hSaria marked this conversation as resolved.
Show resolved Hide resolved


class InterfaceBulkRenameForm(BulkRenameForm):
Expand Down