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

Fixes #4578: Prevent setting 0U height on device type with racked instances

Jeremy Stretch 5 лет назад
Родитель
Сommit
0239be9be5
2 измененных файлов с 22 добавлено и 4 удалено
  1. 1 0
      docs/release-notes/version-2.8.md
  2. 21 4
      netbox/dcim/models/__init__.py

+ 1 - 0
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
 * [#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
 * [#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
 * [#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
 
 
 ---
 ---
 
 

+ 21 - 4
netbox/dcim/models/__init__.py

@@ -12,6 +12,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db import models
 from django.db.models import Count, F, ProtectedError, Sum
 from django.db.models import Count, F, ProtectedError, Sum
 from django.urls import reverse
 from django.urls import reverse
+from django.utils.safestring import mark_safe
 from mptt.models import MPTTModel, TreeForeignKey
 from mptt.models import MPTTModel, TreeForeignKey
 from taggit.managers import TaggableManager
 from taggit.managers import TaggableManager
 from timezone_field import TimeZoneField
 from timezone_field import TimeZoneField
@@ -652,7 +653,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
                 pk=exclude
                 pk=exclude
             ).filter(
             ).filter(
                 rack=self,
                 rack=self,
-                position__gt=0
+                position__gt=0,
+                device_type__u_height__gt=0
             ).filter(
             ).filter(
                 Q(face=face) | Q(device_type__is_full_depth=True)
                 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
         # 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
         # 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.
         # 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):
             for d in Device.objects.filter(device_type=self, position__isnull=False):
                 face_required = None if self.is_full_depth else d.face
                 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:
                 if d.position not in u_available:
                     raise ValidationError({
                     raise ValidationError({
                         'u_height': "Device {} in rack {} does not have sufficient space to accommodate a height of "
                         'u_height': "Device {} in rack {} does not have sufficient space to accommodate a height of "
                                     "{}U".format(d, d.rack, self.u_height)
                                     "{}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 <a href="{url}">{racked_instance_count} instances</a> already '
+                        f'mounted within racks.'
+                    )
+                })
+
         if (
         if (
                 self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT
                 self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT
         ) and self.device_bay_templates.count():
         ) and self.device_bay_templates.count():