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

Fixes #15717 - Unable to assign a VM in Site to Cluster without Site (#15763)

* Fixes #15717: Allow VM with Site to Cluster without Site

* Fixes #15717: Allow VM with Site to Cluster without Site

* Fixes #15717: Allow VM with Site to Cluster without Site

* Fixes #15717: Allow VM with Site to Cluster without Site

* Fixes #15717: Allow VM with Site to Cluster without Site
Ryan Gillespie 1 год назад
Родитель
Сommit
582ede8ed3

+ 4 - 1
netbox/dcim/forms/model_forms.py

@@ -465,7 +465,10 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
         label=_('Cluster'),
         queryset=Cluster.objects.all(),
         required=False,
-        selector=True
+        selector=True,
+        query_params={
+            'site_id': ['$site', 'null']
+        },
     )
     comments = CommentField()
     local_context_data = JSONField(

+ 31 - 0
netbox/dcim/tests/test_models.py

@@ -8,6 +8,7 @@ from dcim.models import *
 from extras.models import CustomField
 from tenancy.models import Tenant
 from utilities.data import drange
+from virtualization.models import Cluster, ClusterType
 
 
 class LocationTestCase(TestCase):
@@ -533,6 +534,36 @@ class DeviceTestCase(TestCase):
         device2.full_clean()
         device2.save()
 
+    def test_device_mismatched_site_cluster(self):
+        cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
+        Cluster.objects.create(name='Cluster 1', type=cluster_type)
+
+        sites = (
+            Site(name='Site 1', slug='site-1'),
+            Site(name='Site 2', slug='site-2'),
+        )
+        Site.objects.bulk_create(sites)
+
+        clusters = (
+            Cluster(name='Cluster 1', type=cluster_type, site=sites[0]),
+            Cluster(name='Cluster 2', type=cluster_type, site=sites[1]),
+            Cluster(name='Cluster 3', type=cluster_type, site=None),
+        )
+        Cluster.objects.bulk_create(clusters)
+
+        device_type = DeviceType.objects.first()
+        device_role = DeviceRole.objects.first()
+
+        # Device with site only should pass
+        Device(name='device1', site=sites[0], device_type=device_type, role=device_role).full_clean()
+
+        # Device with site, cluster non-site should pass
+        Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[2]).full_clean()
+
+        # Device with mismatched site & cluster should fail
+        with self.assertRaises(ValidationError):
+            Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[1]).full_clean()
+
 
 class CableTestCase(TestCase):
 

+ 2 - 2
netbox/virtualization/forms/model_forms.py

@@ -178,8 +178,8 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
         required=False,
         selector=True,
         query_params={
-            'site_id': '$site',
-        }
+            'site_id': ['$site', 'null']
+        },
     )
     device = DynamicModelChoiceField(
         label=_('Device'),

+ 1 - 1
netbox/virtualization/models/virtualmachines.py

@@ -180,7 +180,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
             })
 
         # Validate site for cluster & device
-        if self.cluster and self.site and self.cluster.site != self.site:
+        if self.cluster and self.cluster.site is not None and self.cluster.site != self.site:
             raise ValidationError({
                 'cluster': _(
                     'The selected cluster ({cluster}) is not assigned to this site ({site}).'

+ 3 - 0
netbox/virtualization/tests/test_models.py

@@ -63,6 +63,9 @@ class VirtualMachineTestCase(TestCase):
         # VM with site only should pass
         VirtualMachine(name='vm1', site=sites[0]).full_clean()
 
+        # VM with site, cluster non-site should pass
+        VirtualMachine(name='vm1', site=sites[0], cluster=clusters[2]).full_clean()
+
         # VM with non-site cluster only should pass
         VirtualMachine(name='vm1', cluster=clusters[2]).full_clean()