Flesh out device layout
Some checks are pending
CI / build (20.x, 3.12) (push) Waiting to run
CI / build (20.x, 3.13) (push) Waiting to run

This commit is contained in:
Jeremy Stretch
2025-11-05 16:56:53 -05:00
parent 1d2aef71b2
commit e9777d3193
4 changed files with 101 additions and 3 deletions

View File

@@ -26,7 +26,7 @@ class LocationPanel(panels.NestedGroupObjectPanel):
class RackDimensionsPanel(panels.ObjectAttributesPanel): class RackDimensionsPanel(panels.ObjectAttributesPanel):
form_factor = attrs.ChoiceAttr('form_factor') form_factor = attrs.ChoiceAttr('form_factor')
width = attrs.ChoiceAttr('width') width = attrs.ChoiceAttr('width')
u_height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height')) height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height'))
outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display') outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display')
outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display') outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display')
outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display') outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display')
@@ -76,7 +76,7 @@ class DevicePanel(panels.ObjectAttributesPanel):
site = attrs.ObjectAttr('site', linkify=True, grouped_by='group') site = attrs.ObjectAttr('site', linkify=True, grouped_by='group')
location = attrs.NestedObjectAttr('location', linkify=True) location = attrs.NestedObjectAttr('location', linkify=True)
rack = attrs.TemplatedAttr('rack', template_name='dcim/device/attrs/rack.html') rack = attrs.TemplatedAttr('rack', template_name='dcim/device/attrs/rack.html')
virtual_chassis = attrs.NestedObjectAttr('virtual_chassis', linkify=True) virtual_chassis = attrs.ObjectAttr('virtual_chassis', linkify=True)
parent_device = attrs.TemplatedAttr('parent_bay', template_name='dcim/device/attrs/parent_device.html') parent_device = attrs.TemplatedAttr('parent_bay', template_name='dcim/device/attrs/parent_device.html')
gps_coordinates = attrs.GPSCoordinatesAttr() gps_coordinates = attrs.GPSCoordinatesAttr()
tenant = attrs.ObjectAttr('tenant', linkify=True, grouped_by='group') tenant = attrs.ObjectAttr('tenant', linkify=True, grouped_by='group')
@@ -107,6 +107,12 @@ class DeviceManagementPanel(panels.ObjectAttributesPanel):
label=_('Out-of-band IP'), label=_('Out-of-band IP'),
template_name='dcim/device/attrs/ipaddress.html', template_name='dcim/device/attrs/ipaddress.html',
) )
cluster = attrs.ObjectAttr('cluster', linkify=True)
class DeviceDimensionsPanel(panels.ObjectAttributesPanel):
height = attrs.TextAttr('device_type.u_height', format_string='{}U')
total_weight = attrs.TemplatedAttr('total_weight', template_name='dcim/device/attrs/total_weight.html')
class DeviceTypePanel(panels.ObjectAttributesPanel): class DeviceTypePanel(panels.ObjectAttributesPanel):
@@ -115,7 +121,7 @@ class DeviceTypePanel(panels.ObjectAttributesPanel):
part_number = attrs.TextAttr('part_number') part_number = attrs.TextAttr('part_number')
default_platform = attrs.ObjectAttr('default_platform', linkify=True) default_platform = attrs.ObjectAttr('default_platform', linkify=True)
description = attrs.TextAttr('description') description = attrs.TextAttr('description')
u_height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height')) height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height'))
exclude_from_utilization = attrs.BooleanAttr('exclude_from_utilization') exclude_from_utilization = attrs.BooleanAttr('exclude_from_utilization')
full_depth = attrs.BooleanAttr('is_full_depth') full_depth = attrs.BooleanAttr('is_full_depth')
weight = attrs.NumericAttr('weight', unit_accessor='get_weight_unit_display') weight = attrs.NumericAttr('weight', unit_accessor='get_weight_unit_display')
@@ -128,3 +134,23 @@ class DeviceTypePanel(panels.ObjectAttributesPanel):
class ModuleTypeProfilePanel(panels.ObjectAttributesPanel): class ModuleTypeProfilePanel(panels.ObjectAttributesPanel):
name = attrs.TextAttr('name') name = attrs.TextAttr('name')
description = attrs.TextAttr('description') description = attrs.TextAttr('description')
class VirtualChassisMembersPanel(panels.ObjectPanel):
"""
A panel which lists all members of a virtual chassis.
"""
template_name = 'dcim/panels/virtual_chassis_members.html'
title = _('Virtual Chassis Members')
def get_context(self, context):
"""
Return the context data to be used when rendering the panel.
Parameters:
context: The template context
"""
return {
**super().get_context(context),
'vc_members': context.get('vc_members'),
}

View File

@@ -2439,6 +2439,44 @@ class DeviceListView(generic.ObjectListView):
@register_model_view(Device) @register_model_view(Device)
class DeviceView(generic.ObjectView): class DeviceView(generic.ObjectView):
queryset = Device.objects.all() queryset = Device.objects.all()
layout = layout.SimpleLayout(
left_panels=[
panels.DevicePanel(),
panels.VirtualChassisMembersPanel(),
CustomFieldsPanel(),
TagsPanel(),
CommentsPanel(),
ObjectsTablePanel(
model='dcim.VirtualDeviceContext',
filters={'device_id': lambda ctx: ctx['object'].pk},
actions=[
actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}),
],
),
],
right_panels=[
panels.DeviceManagementPanel(),
# TODO: Power utilization
ObjectsTablePanel(
model='ipam.Service',
title=_('Application Services'),
filters={'device_id': lambda ctx: ctx['object'].pk},
actions=[
actions.AddObject(
'ipam.Service',
url_params={
'parent_object_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
'parent': lambda ctx: ctx['object'].pk
}
),
],
),
ImageAttachmentsPanel(),
panels.DeviceDimensionsPanel(title=_('Dimensions')),
# TODO: Rack elevations
# TemplatePanel('dcim/panels/rack_elevations.html'),
],
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
# VirtualChassis members # VirtualChassis members

View File

@@ -0,0 +1,3 @@
{% load helpers i18n %}
{{ value|floatformat }} {% trans "Kilograms" %}
({{ value|kg_to_pounds|floatformat }} {% trans "Pounds" %})

View File

@@ -0,0 +1,31 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<table class="table table-hover attr-table">
<thead>
<tr class="border-bottom">
<th>{% trans "Device" %}</th>
<th>{% trans "Position" %}</th>
<th>{% trans "Master" %}</th>
<th>{% trans "Priority" %}</th>
</tr>
</thead>
<tbody>
{% for vc_member in vc_members %}
<tr{% if vc_member == object %} class="table-primary"{% endif %}>
<td>{{ vc_member|linkify }}</td>
<td>{% badge vc_member.vc_position show_empty=True %}</td>
<td>
{% if object.virtual_chassis.master == vc_member %}
{% checkmark True %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
<td>{{ vc_member.vc_priority|placeholder }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock panel_content %}