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

Closes #4940: Added an occupied field to rack unit representations for rack elevation views

Jeremy Stretch 5 лет назад
Родитель
Сommit
31e65a09e8

+ 2 - 0
docs/release-notes/version-2.9.md

@@ -5,6 +5,7 @@
 ### Enhancements
 
 * [#4919](https://github.com/netbox-community/netbox/issues/4919) - Allow adding/changing assigned permissions within group and user admin views
+* [#4940](https://github.com/netbox-community/netbox/issues/4940) - Added an `occupied` field to rack unit representations for rack elevation views
 
 ### Bug Fixes
 
@@ -86,6 +87,7 @@ When running a report or custom script, its execution is now queued for backgrou
 * dcim.PowerPortTemplate: Added `description` and `label` fields
 * dcim.PowerOutlet: Added `label` field
 * dcim.PowerOutletTemplate: Added `description` and `label` fields
+* dcim.Rack: Added an `occupied` field to rack unit representations for rack elevation views
 * dcim.RackGroup: Added a `_depth` attribute indicating an object's position in the tree.
 * dcim.RackReservation: Added `tags` field
 * dcim.RearPort: Added `label` field

+ 1 - 0
netbox/dcim/api/serializers.py

@@ -165,6 +165,7 @@ class RackUnitSerializer(serializers.Serializer):
     name = serializers.CharField(read_only=True)
     face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
     device = NestedDeviceSerializer(read_only=True)
+    occupied = serializers.BooleanField(read_only=True)
 
 
 class RackReservationSerializer(TaggedObjectSerializer, ValidatedModelSerializer):

+ 1 - 0
netbox/dcim/api/views.py

@@ -189,6 +189,7 @@ class RackViewSet(CustomFieldModelViewSet):
             # Return a JSON representation of the rack units in the elevation
             elevation = rack.get_rack_units(
                 face=data['face'],
+                user=request.user,
                 exclude=data['exclude'],
                 expand_devices=data['expand_devices']
             )

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

@@ -656,12 +656,14 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
     def get_status_class(self):
         return self.STATUS_CLASS_MAP.get(self.status)
 
-    def get_rack_units(self, face=DeviceFaceChoices.FACE_FRONT, exclude=None, expand_devices=True):
+    def get_rack_units(self, user=None, face=DeviceFaceChoices.FACE_FRONT, exclude=None, expand_devices=True):
         """
         Return a list of rack units as dictionaries. Example: {'device': None, 'face': 0, 'id': 48, 'name': 'U48'}
         Each key 'device' is either a Device or None. By default, multi-U devices are repeated for each U they occupy.
 
         :param face: Rack face (front or rear)
+        :param user: User instance to be used for evaluating device view permissions. If None, all devices
+            will be included.
         :param exclude: PK of a Device to exclude (optional); helpful when relocating a Device within a Rack
         :param expand_devices: When True, all units that a device occupies will be listed with each containing a
             reference to the device. When False, only the bottom most unit for a device is included and that unit
@@ -670,10 +672,18 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
 
         elevation = OrderedDict()
         for u in self.units:
-            elevation[u] = {'id': u, 'name': 'U{}'.format(u), 'face': face, 'device': None}
+            elevation[u] = {
+                'id': u,
+                'name': f'U{u}',
+                'face': face,
+                'device': None,
+                'occupied': False
+            }
 
         # Add devices to rack units list
         if self.pk:
+
+            # Retrieve all devices installed within the rack
             queryset = Device.objects.prefetch_related(
                 'device_type',
                 'device_type__manufacturer',
@@ -689,12 +699,22 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
             ).filter(
                 Q(face=face) | Q(device_type__is_full_depth=True)
             )
+
+            # Determine which devices the user has permission to view
+            permitted_device_ids = []
+            if user is not None:
+                permitted_device_ids = self.devices.restrict(user, 'view').values_list('pk', flat=True)
+
             for device in queryset:
                 if expand_devices:
                     for u in range(device.position, device.position + device.device_type.u_height):
-                        elevation[u]['device'] = device
+                        if user is None or device.pk in permitted_device_ids:
+                            elevation[u]['device'] = device
+                        elevation[u]['occupied'] = True
                 else:
-                    elevation[device.position]['device'] = device
+                    if user is None or device.pk in permitted_device_ids:
+                        elevation[device.position]['device'] = device
+                    elevation[device.position]['occupied'] = True
                     elevation[device.position]['height'] = device.device_type.u_height
                     for u in range(device.position + 1, device.position + device.device_type.u_height):
                         elevation.pop(u, None)