Просмотр исходного кода

Closes #12622: Fix assigning VLAN without site to Prefix (#12784)

* 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
Dillon Henschen 2 лет назад
Родитель
Сommit
0e873a01b8
3 измененных файлов с 85 добавлено и 12 удалено
  1. 25 9
      netbox/ipam/forms/bulk_import.py
  2. 1 3
      netbox/ipam/forms/model_forms.py
  3. 59 0
      netbox/ipam/tests/test_views.py

+ 25 - 9
netbox/ipam/forms/bulk_import.py

@@ -1,6 +1,7 @@
 from django import forms
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
+from django.db.models import Q
 from django.utils.translation import gettext as _
 
 from dcim.models import Device, Interface, Site
@@ -181,16 +182,31 @@ class PrefixImportForm(NetBoxModelImportForm):
     def __init__(self, data=None, *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)
+        query = Q()
+
+        if site:
+            query |= Q(**{
+                f"site__{self.fields['site'].to_field_name}": site
+            })
+            # 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
+            })
 
-            # Limit VLAN queryset by assigned site and/or group (if specified)
-            params = {}
-            if data.get('site'):
-                params[f"site__{self.fields['site'].to_field_name}"] = data.get('site')
-            if data.get('vlan_group'):
-                params[f"group__{self.fields['vlan_group'].to_field_name}"] = data.get('vlan_group')
-            if params:
-                self.fields['vlan'].queryset = self.fields['vlan'].queryset.filter(**params)
+        queryset = self.fields['vlan'].queryset.filter(query)
+        self.fields['vlan'].queryset = queryset
 
 
 class IPRangeImportForm(NetBoxModelImportForm):

+ 1 - 3
netbox/ipam/forms/model_forms.py

@@ -211,10 +211,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
     vlan = DynamicModelChoiceField(
         queryset=VLAN.objects.all(),
         required=False,
+        selector=True,
         label=_('VLAN'),
-        query_params={
-            'site_id': '$site',
-        }
     )
     role = DynamicModelChoiceField(
         queryset=Role.objects.all(),

+ 59 - 0
netbox/ipam/tests/test_views.py

@@ -495,6 +495,65 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk})
         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):
     model = IPRange