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

Closes #19273: Enable selection of VLANs scoped to a Device's Cluster or Cluster Group (#22715)

Martin Hauser 1 неделя назад
Родитель
Сommit
15d2cc35e0
3 измененных файлов с 33 добавлено и 1 удалено
  1. 1 1
      netbox/ipam/filtersets.py
  2. 12 0
      netbox/ipam/querysets.py
  3. 20 0
      netbox/ipam/tests/test_filtersets.py

+ 1 - 1
netbox/ipam/filtersets.py

@@ -1070,7 +1070,7 @@ class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
         method='get_for_site'
     )
     available_on_device = django_filters.ModelChoiceFilter(
-        queryset=Device.objects.all(),
+        queryset=Device.objects.select_related('cluster'),
         method='get_for_device'
     )
     available_on_virtualmachine = django_filters.ModelChoiceFilter(

+ 12 - 0
netbox/ipam/querysets.py

@@ -298,6 +298,18 @@ class VLANQuerySet(RestrictedQuerySet):
 
         # Find all relevant VLANGroups
         q = Q()
+        if device.cluster_id:
+            # The Device's physical scope is evaluated below. For valid assignments,
+            # the Cluster's physical scope is already represented by that hierarchy.
+            q |= Q(
+                scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'),
+                scope_id=device.cluster_id
+            )
+            if device.cluster.group_id:
+                q |= Q(
+                    scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'),
+                    scope_id=device.cluster.group_id
+                )
         if device.site.region:
             q |= Q(
                 scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'),

+ 20 - 0
netbox/ipam/tests/test_filtersets.py

@@ -2188,6 +2188,26 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'available_on_device': device_id}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7)  # 5 scoped + 1 global group + 1 global
 
+    def test_available_on_device_cluster_scopes(self):
+        device = Device.objects.get(name='Device 1')
+        device.cluster = Cluster.objects.get(name='Cluster 1')
+        device.save(update_fields=('cluster',))
+
+        params = {'available_on_device': device.pk}
+        vlans = self.filterset(params, self.queryset).qs
+
+        # VLANs from groups scoped to the assigned cluster or its cluster group
+        self.assertIn(VLAN.objects.get(name='Cluster 1'), vlans)
+        self.assertIn(VLAN.objects.get(name='Cluster Group 1'), vlans)
+        # VLANs from groups scoped to unrelated clusters or cluster groups
+        self.assertNotIn(VLAN.objects.get(name='Cluster 2'), vlans)
+        self.assertNotIn(VLAN.objects.get(name='Cluster Group 2'), vlans)
+        # Site, location, rack and global availability is unchanged
+        self.assertEqual(
+            set(vlans.values_list('vid', flat=True)),
+            {1, 4, 7, 10, 13, 16, 19, 500, 1000}
+        )
+
     def test_available_on_virtualmachine(self):
         vm_id = VirtualMachine.objects.first().pk
         params = {'available_on_virtualmachine': vm_id}