Jeremy Stretch 3 месяцев назад
Родитель
Сommit
e55a4ae603

+ 28 - 5
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.
+        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)
 
-        Parameters:
-            context: The template 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)

+ 3 - 4
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'),
         ],
     )
 

+ 26 - 0
netbox/templates/dcim/panels/device_rack_elevations.html

@@ -0,0 +1,26 @@
+{% load i18n %}
+{% if object.rack and object.position %}
+  <div class="row" style="margin-bottom: 20px">
+    <div class="text-center">
+      <strong><a href="{% url 'dcim:rack' pk=object.rack.pk %}">{{ object.rack.name }}</a></strong>
+      {% if object.rack.role %}
+        <br /><span class="badge my-3" style="color: {{ object.rack.role.color|fgcolor }}; background-color: #{{ object.rack.role.color }}">{{ object.rack.role }}</span>
+      {% endif %}
+      {% if object.rack.facility_id %}
+        <br /><small class="text-muted">{{ object.rack.facility_id }}</small>
+      {% endif %}
+    </div>
+    <div class="col col-md-6 col-sm-6 col-xs-12 text-center">
+      <div style="margin-left: 30px">
+        <h2 class="h4">{% trans "Front" %}</h2>
+        {% include 'dcim/inc/rack_elevation.html' with object=object.rack face='front' extra_params=svg_extra %}
+      </div>
+    </div>
+    <div class="col col-md-6 col-sm-6 col-xs-12 text-center">
+      <div style="margin-left: 30px">
+        <h2 class="h4">{% trans "Rear" %}</h2>
+        {% include 'dcim/inc/rack_elevation.html' with object=object.rack face='rear' extra_params=svg_extra %}
+      </div>
+    </div>
+  </div>
+{% endif %}

+ 50 - 0
netbox/templates/dcim/panels/power_utilization.html

@@ -0,0 +1,50 @@
+{% extends "ui/panels/_base.html" %}
+{% load helpers i18n %}
+
+{% block panel_content %}
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>{% trans "Input" %}</th>
+        <th>{% trans "Outlets" %}</th>
+        <th>{% trans "Allocated" %}</th>
+        <th>{% trans "Available" %}</th>
+        <th>{% trans "Utilization" %}</th>
+      </tr>
+    </thead>
+    {% for powerport in object.powerports.all %}
+      {% with utilization=powerport.get_power_draw powerfeed=powerport.connected_endpoints.0 %}
+        <tr>
+          <td>{{ powerport }}</td>
+          <td>{{ utilization.outlet_count }}</td>
+          <td>{{ utilization.allocated }}{% trans "VA" %}</td>
+          {% if powerfeed.available_power %}
+            <td>{{ powerfeed.available_power }}{% trans "VA" %}</td>
+            <td>{% utilization_graph utilization.allocated|percentage:powerfeed.available_power %}</td>
+          {% else %}
+            <td class="text-muted">&mdash;</td>
+            <td class="text-muted">&mdash;</td>
+          {% endif %}
+        </tr>
+        {% for leg in utilization.legs %}
+          <tr>
+            <td style="padding-left: 20px">
+              {% trans "Leg" context "Leg of a power feed" %} {{ leg.name }}
+            </td>
+            <td>{{ leg.outlet_count }}</td>
+            <td>{{ leg.allocated }}</td>
+            {% if powerfeed.available_power %}
+              {% with phase_available=powerfeed.available_power|divide:3 %}
+                <td>{{ phase_available }}{% trans "VA" %}</td>
+                <td>{% utilization_graph leg.allocated|percentage:phase_available %}</td>
+              {% endwith %}
+            {% else %}
+              <td class="text-muted">&mdash;</td>
+              <td class="text-muted">&mdash;</td>
+            {% endif %}
+          </tr>
+        {% endfor %}
+      {% endwith %}
+    {% endfor %}
+  </table>
+{% endblock panel_content %}