mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* Issue #12622: Fix creating Prefix using VLAN without site * Issue #12622: Fix importing Prefix using VLAN without site This commit also adds tests to verify the import changes implemented in this commit. * Issue #12622: Cleanup code to filter allowed VLANs on a prefix import * Closes #12622: Switch to VLAN selector dialog when creating Prefix
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Site
|
from dcim.models import Device, Interface, Site
|
||||||
@ -181,16 +182,31 @@ class PrefixImportForm(NetBoxModelImportForm):
|
|||||||
def __init__(self, data=None, *args, **kwargs):
|
def __init__(self, data=None, *args, **kwargs):
|
||||||
super().__init__(data, *args, **kwargs)
|
super().__init__(data, *args, **kwargs)
|
||||||
|
|
||||||
if data:
|
if not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
site = data.get('site')
|
||||||
|
vlan_group = data.get('vlan_group')
|
||||||
|
|
||||||
# Limit VLAN queryset by assigned site and/or group (if specified)
|
# Limit VLAN queryset by assigned site and/or group (if specified)
|
||||||
params = {}
|
query = Q()
|
||||||
if data.get('site'):
|
|
||||||
params[f"site__{self.fields['site'].to_field_name}"] = data.get('site')
|
if site:
|
||||||
if data.get('vlan_group'):
|
query |= Q(**{
|
||||||
params[f"group__{self.fields['vlan_group'].to_field_name}"] = data.get('vlan_group')
|
f"site__{self.fields['site'].to_field_name}": site
|
||||||
if params:
|
})
|
||||||
self.fields['vlan'].queryset = self.fields['vlan'].queryset.filter(**params)
|
# Don't Forget to include VLANs without a site in the filter
|
||||||
|
query |= Q(**{
|
||||||
|
f"site__{self.fields['site'].to_field_name}__isnull": True
|
||||||
|
})
|
||||||
|
|
||||||
|
if vlan_group:
|
||||||
|
query &= Q(**{
|
||||||
|
f"group__{self.fields['vlan_group'].to_field_name}": vlan_group
|
||||||
|
})
|
||||||
|
|
||||||
|
queryset = self.fields['vlan'].queryset.filter(query)
|
||||||
|
self.fields['vlan'].queryset = queryset
|
||||||
|
|
||||||
|
|
||||||
class IPRangeImportForm(NetBoxModelImportForm):
|
class IPRangeImportForm(NetBoxModelImportForm):
|
||||||
|
@ -211,10 +211,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
|
|||||||
vlan = DynamicModelChoiceField(
|
vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
selector=True,
|
||||||
label=_('VLAN'),
|
label=_('VLAN'),
|
||||||
query_params={
|
|
||||||
'site_id': '$site',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
role = DynamicModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
|
@ -495,6 +495,65 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk})
|
url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk})
|
||||||
self.assertHttpStatus(self.client.get(url), 200)
|
self.assertHttpStatus(self.client.get(url), 200)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_prefix_import(self):
|
||||||
|
"""
|
||||||
|
Custom import test for YAML-based imports (versus CSV)
|
||||||
|
"""
|
||||||
|
IMPORT_DATA = """
|
||||||
|
prefix: 10.1.1.0/24
|
||||||
|
status: active
|
||||||
|
vlan: 101
|
||||||
|
site: Site 1
|
||||||
|
"""
|
||||||
|
# Note, a site is not tied to the VLAN to verify the fix for #12622
|
||||||
|
VLAN.objects.create(vid=101, name='VLAN101')
|
||||||
|
|
||||||
|
# Add all required permissions to the test user
|
||||||
|
self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'data': IMPORT_DATA,
|
||||||
|
'format': 'yaml'
|
||||||
|
}
|
||||||
|
response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True)
|
||||||
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
prefix = Prefix.objects.get(prefix='10.1.1.0/24')
|
||||||
|
self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
|
||||||
|
self.assertEqual(prefix.vlan.vid, 101)
|
||||||
|
self.assertEqual(prefix.site.name, "Site 1")
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_prefix_import_with_vlan_group(self):
|
||||||
|
"""
|
||||||
|
This test covers a unique import edge case where VLAN group is specified during the import.
|
||||||
|
"""
|
||||||
|
IMPORT_DATA = """
|
||||||
|
prefix: 10.1.2.0/24
|
||||||
|
status: active
|
||||||
|
vlan: 102
|
||||||
|
site: Site 1
|
||||||
|
vlan_group: Group 1
|
||||||
|
"""
|
||||||
|
vlan_group = VLANGroup.objects.create(name='Group 1', slug='group-1', scope=Site.objects.get(name="Site 1"))
|
||||||
|
VLAN.objects.create(vid=102, name='VLAN102', group=vlan_group)
|
||||||
|
|
||||||
|
# Add all required permissions to the test user
|
||||||
|
self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'data': IMPORT_DATA,
|
||||||
|
'format': 'yaml'
|
||||||
|
}
|
||||||
|
response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True)
|
||||||
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
prefix = Prefix.objects.get(prefix='10.1.2.0/24')
|
||||||
|
self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
|
||||||
|
self.assertEqual(prefix.vlan.vid, 102)
|
||||||
|
self.assertEqual(prefix.site.name, "Site 1")
|
||||||
|
|
||||||
|
|
||||||
class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = IPRange
|
model = IPRange
|
||||||
|
Reference in New Issue
Block a user