mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 04:02:52 -06:00
Closes #4940: Added an occupied field to rack unit representations for rack elevation views
This commit is contained in:
parent
cdfec4d865
commit
31e65a09e8
@ -5,6 +5,7 @@
|
|||||||
### Enhancements
|
### Enhancements
|
||||||
|
|
||||||
* [#4919](https://github.com/netbox-community/netbox/issues/4919) - Allow adding/changing assigned permissions within group and user admin views
|
* [#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
|
### 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.PowerPortTemplate: Added `description` and `label` fields
|
||||||
* dcim.PowerOutlet: Added `label` field
|
* dcim.PowerOutlet: Added `label` field
|
||||||
* dcim.PowerOutletTemplate: Added `description` and `label` fields
|
* 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.RackGroup: Added a `_depth` attribute indicating an object's position in the tree.
|
||||||
* dcim.RackReservation: Added `tags` field
|
* dcim.RackReservation: Added `tags` field
|
||||||
* dcim.RearPort: Added `label` field
|
* dcim.RearPort: Added `label` field
|
||||||
|
@ -165,6 +165,7 @@ class RackUnitSerializer(serializers.Serializer):
|
|||||||
name = serializers.CharField(read_only=True)
|
name = serializers.CharField(read_only=True)
|
||||||
face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
|
face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
|
occupied = serializers.BooleanField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class RackReservationSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
class RackReservationSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
||||||
|
@ -189,6 +189,7 @@ class RackViewSet(CustomFieldModelViewSet):
|
|||||||
# Return a JSON representation of the rack units in the elevation
|
# Return a JSON representation of the rack units in the elevation
|
||||||
elevation = rack.get_rack_units(
|
elevation = rack.get_rack_units(
|
||||||
face=data['face'],
|
face=data['face'],
|
||||||
|
user=request.user,
|
||||||
exclude=data['exclude'],
|
exclude=data['exclude'],
|
||||||
expand_devices=data['expand_devices']
|
expand_devices=data['expand_devices']
|
||||||
)
|
)
|
||||||
|
@ -656,12 +656,14 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
def get_status_class(self):
|
def get_status_class(self):
|
||||||
return self.STATUS_CLASS_MAP.get(self.status)
|
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'}
|
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.
|
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 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 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
|
: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
|
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()
|
elevation = OrderedDict()
|
||||||
for u in self.units:
|
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
|
# Add devices to rack units list
|
||||||
if self.pk:
|
if self.pk:
|
||||||
|
|
||||||
|
# Retrieve all devices installed within the rack
|
||||||
queryset = Device.objects.prefetch_related(
|
queryset = Device.objects.prefetch_related(
|
||||||
'device_type',
|
'device_type',
|
||||||
'device_type__manufacturer',
|
'device_type__manufacturer',
|
||||||
@ -689,12 +699,22 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
).filter(
|
).filter(
|
||||||
Q(face=face) | Q(device_type__is_full_depth=True)
|
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:
|
for device in queryset:
|
||||||
if expand_devices:
|
if expand_devices:
|
||||||
for u in range(device.position, device.position + device.device_type.u_height):
|
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:
|
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
|
elevation[device.position]['height'] = device.device_type.u_height
|
||||||
for u in range(device.position + 1, device.position + device.device_type.u_height):
|
for u in range(device.position + 1, device.position + device.device_type.u_height):
|
||||||
elevation.pop(u, None)
|
elevation.pop(u, None)
|
||||||
|
Loading…
Reference in New Issue
Block a user