diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 88d094b97..e8f82d58e 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -946,7 +946,7 @@ class Device( if self.cluster and self.cluster._location is not None and self.cluster._location != self.location: raise ValidationError({ 'cluster': _("The assigned cluster belongs to a different location ({location})").format( - site=self.cluster._location + location=self.cluster._location ) }) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index a4d6c7bf0..d450049a6 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -855,6 +855,50 @@ class DeviceTestCase(TestCase): cluster=clusters[1] ).full_clean() + @tag('regression') # Ref: #22717 + def test_device_mismatched_location_cluster(self): + """ + A cluster scoped to a different location than the device must be rejected + with a field validation error naming that location. + """ + site = Site.objects.create(name='Site 1', slug='site-1') + locations = ( + Location(site=site, name='Location A', slug='location-a'), + Location(site=site, name='Location B', slug='location-b'), + ) + for location in locations: + location.save() + + cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1') + cluster = Cluster.objects.create(name='Cluster 1', type=cluster_type, scope=locations[0]) + + device_type = DeviceType.objects.first() + device_role = DeviceRole.objects.first() + + # Device in the cluster's location should pass + Device( + name='device1', + site=site, + location=locations[0], + device_type=device_type, + role=device_role, + cluster=cluster + ).full_clean() + + # Device in a different location of the same site should fail + with self.assertRaisesMessage( + ValidationError, + 'The assigned cluster belongs to a different location (Location A)' + ): + Device( + name='device1', + site=site, + location=locations[1], + device_type=device_type, + role=device_role, + cluster=cluster + ).full_clean() + class DeviceBayTestCase(TestCase):