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
This commit is contained in:
Ryan Gillespie 2024-06-20 08:59:17 -06:00 committed by GitHub
parent 32e219c70a
commit 582ede8ed3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 41 additions and 4 deletions

View File

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

View File

@ -8,6 +8,7 @@ from dcim.models import *
from extras.models import CustomField from extras.models import CustomField
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.data import drange from utilities.data import drange
from virtualization.models import Cluster, ClusterType
class LocationTestCase(TestCase): class LocationTestCase(TestCase):
@ -533,6 +534,36 @@ class DeviceTestCase(TestCase):
device2.full_clean() device2.full_clean()
device2.save() 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): class CableTestCase(TestCase):

View File

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

View File

@ -180,7 +180,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
}) })
# Validate site for cluster & device # 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({ raise ValidationError({
'cluster': _( 'cluster': _(
'The selected cluster ({cluster}) is not assigned to this site ({site}).' 'The selected cluster ({cluster}) is not assigned to this site ({site}).'

View File

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