From 0239be9be5603d571e813f1fe062cf651390854b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 5 May 2020 13:41:23 -0400 Subject: [PATCH] Fixes #4578: Prevent setting 0U height on device type with racked instances --- docs/release-notes/version-2.8.md | 1 + netbox/dcim/models/__init__.py | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-2.8.md b/docs/release-notes/version-2.8.md index 0d828105d..0ca89bf61 100644 --- a/docs/release-notes/version-2.8.md +++ b/docs/release-notes/version-2.8.md @@ -18,6 +18,7 @@ * [#4548](https://github.com/netbox-community/netbox/issues/4548) - Fix tracing cables through a single RearPort * [#4549](https://github.com/netbox-community/netbox/issues/4549) - Fix encoding unicode webhook body data * [#4556](https://github.com/netbox-community/netbox/issues/4556) - Update form for adding devices to clusters +* [#4578](https://github.com/netbox-community/netbox/issues/4578) - Prevent setting 0U height on device type with racked instances --- diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 4b30d20d1..0af4ef6a4 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -12,6 +12,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.db.models import Count, F, ProtectedError, Sum from django.urls import reverse +from django.utils.safestring import mark_safe from mptt.models import MPTTModel, TreeForeignKey from taggit.managers import TaggableManager from timezone_field import TimeZoneField @@ -652,7 +653,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel): pk=exclude ).filter( rack=self, - position__gt=0 + position__gt=0, + device_type__u_height__gt=0 ).filter( Q(face=face) | Q(device_type__is_full_depth=True) ) @@ -1089,17 +1091,32 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel): # If editing an existing DeviceType to have a larger u_height, first validate that *all* instances of it have # room to expand within their racks. This validation will impose a very high performance penalty when there are # many instances to check, but increasing the u_height of a DeviceType should be a very rare occurrence. - if self.pk is not None and self.u_height > self._original_u_height: + if self.pk and self.u_height > self._original_u_height: for d in Device.objects.filter(device_type=self, position__isnull=False): face_required = None if self.is_full_depth else d.face - u_available = d.rack.get_available_units(u_height=self.u_height, rack_face=face_required, - exclude=[d.pk]) + u_available = d.rack.get_available_units( + u_height=self.u_height, + rack_face=face_required, + exclude=[d.pk] + ) if d.position not in u_available: raise ValidationError({ 'u_height': "Device {} in rack {} does not have sufficient space to accommodate a height of " "{}U".format(d, d.rack, self.u_height) }) + # If modifying the height of an existing DeviceType to 0U, check for any instances assigned to a rack position. + elif self.pk and self._original_u_height > 0 and self.u_height == 0: + racked_instance_count = Device.objects.filter(device_type=self, position__isnull=False).count() + if racked_instance_count: + url = f"{reverse('dcim:device_list')}?manufactuer_id={self.manufacturer_id}&device_type_id={self.pk}" + raise ValidationError({ + 'u_height': mark_safe( + f'Unable to set 0U height: Found {racked_instance_count} instances already ' + f'mounted within racks.' + ) + }) + if ( self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT ) and self.device_bay_templates.count():