diff --git a/netbox/dcim/ui/panels.py b/netbox/dcim/ui/panels.py index e4845bfc0..62435bedf 100644 --- a/netbox/dcim/ui/panels.py +++ b/netbox/dcim/ui/panels.py @@ -89,6 +89,8 @@ class DevicePanel(panels.ObjectAttributesPanel): class DeviceManagementPanel(panels.ObjectAttributesPanel): + title = _('Management') + status = attrs.ChoiceAttr('status') role = attrs.NestedObjectAttr('role', linkify=True, max_depth=3) platform = attrs.NestedObjectAttr('platform', linkify=True, max_depth=3) @@ -111,6 +113,8 @@ class DeviceManagementPanel(panels.ObjectAttributesPanel): class DeviceDimensionsPanel(panels.ObjectAttributesPanel): + title = _('Dimensions') + height = attrs.TextAttr('device_type.u_height', format_string='{}U') total_weight = attrs.TemplatedAttr('total_weight', template_name='dcim/device/attrs/total_weight.html') @@ -144,13 +148,32 @@ class VirtualChassisMembersPanel(panels.ObjectPanel): 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'), } + + def render(self, context): + if not context.get('vc_members'): + return '' + return super().render(context) + + +class PowerUtilizationPanel(panels.ObjectPanel): + """ + A panel which displays the power utilization statistics for a device. + """ + template_name = 'dcim/panels/power_utilization.html' + title = _('Power Utilization') + + def get_context(self, context): + return { + **super().get_context(context), + 'vc_members': context.get('vc_members'), + } + + def render(self, context): + obj = context['object'] + if not obj.powerports.exists() or not obj.poweroutlets.exists(): + return '' + return super().render(context) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 9f7b3f06a..b8a9e134a 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -2456,7 +2456,7 @@ class DeviceView(generic.ObjectView): ], right_panels=[ panels.DeviceManagementPanel(), - # TODO: Power utilization + panels.PowerUtilizationPanel(), ObjectsTablePanel( model='ipam.Service', title=_('Application Services'), @@ -2472,9 +2472,8 @@ class DeviceView(generic.ObjectView): ], ), ImageAttachmentsPanel(), - panels.DeviceDimensionsPanel(title=_('Dimensions')), - # TODO: Rack elevations - # TemplatePanel('dcim/panels/rack_elevations.html'), + panels.DeviceDimensionsPanel(), + TemplatePanel('dcim/panels/device_rack_elevations.html'), ], ) diff --git a/netbox/templates/dcim/panels/device_rack_elevations.html b/netbox/templates/dcim/panels/device_rack_elevations.html new file mode 100644 index 000000000..1816be5c9 --- /dev/null +++ b/netbox/templates/dcim/panels/device_rack_elevations.html @@ -0,0 +1,26 @@ +{% load i18n %} +{% if object.rack and object.position %} +
| {% trans "Input" %} | +{% trans "Outlets" %} | +{% trans "Allocated" %} | +{% trans "Available" %} | +{% trans "Utilization" %} | +||
|---|---|---|---|---|---|---|
| {{ powerport }} | +{{ utilization.outlet_count }} | +{{ utilization.allocated }}{% trans "VA" %} | + {% if powerfeed.available_power %} +{{ powerfeed.available_power }}{% trans "VA" %} | +{% utilization_graph utilization.allocated|percentage:powerfeed.available_power %} | + {% else %} +— | +— | + {% endif %} +
| + {% trans "Leg" context "Leg of a power feed" %} {{ leg.name }} + | +{{ leg.outlet_count }} | +{{ leg.allocated }} | + {% if powerfeed.available_power %} + {% with phase_available=powerfeed.available_power|divide:3 %} +{{ phase_available }}{% trans "VA" %} | +{% utilization_graph leg.allocated|percentage:phase_available %} | + {% endwith %} + {% else %} +— | +— | + {% endif %} +