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

First stab at power utilization tracking

Jeremy Stretch 6 лет назад
Родитель
Сommit
ac3e899f5e
2 измененных файлов с 86 добавлено и 1 удалено
  1. 43 1
      netbox/dcim/models.py
  2. 43 0
      netbox/templates/dcim/device.html

+ 43 - 1
netbox/dcim/models.py

@@ -9,7 +9,7 @@ from django.contrib.postgres.fields import ArrayField, JSONField
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
-from django.db.models import Count, Q
+from django.db.models import Count, Q, Sum
 from django.urls import reverse
 from mptt.models import MPTTModel, TreeForeignKey
 from taggit.managers import TaggableManager
@@ -1938,6 +1938,41 @@ class PowerPort(CableTermination, ComponentModel):
                 "Connected endpoint must be a PowerOutlet or PowerFeed, not {}.".format(type(value))
             )
 
+    def get_power_stats(self):
+        """
+        Return power utilization statistics
+        """
+        feed = self._connected_powerfeed
+        if not feed or not self.poweroutlets.count():
+            return None
+
+        stats = []
+        powerfeed_available = self._connected_powerfeed.available_power
+
+        outlet_ids = PowerOutlet.objects.filter(power_port=self).values_list('pk', flat=True)
+        utilization = PowerPort.objects.filter(_connected_poweroutlet_id__in=outlet_ids).aggregate(
+            maximum_draw=Sum('maximum_draw'),
+            allocated_draw=Sum('allocated_draw'),
+        )
+        utilization['outlets'] = len(outlet_ids)
+        utilization['available_power'] = powerfeed_available
+        stats.append(utilization)
+
+        # Per-leg stats for three-phase feeds
+        if feed.phase == POWERFEED_PHASE_3PHASE:
+            for leg, leg_name in POWERFEED_LEG_CHOICES:
+                outlet_ids = PowerOutlet.objects.filter(power_port=self, feed_leg=leg).values_list('pk', flat=True)
+                utilization = PowerPort.objects.filter(_connected_poweroutlet_id__in=outlet_ids).aggregate(
+                    maximum_draw=Sum('maximum_draw'),
+                    allocated_draw=Sum('allocated_draw'),
+                )
+                utilization['name'] = 'Leg {}'.format(leg_name)
+                utilization['outlets'] = len(outlet_ids)
+                utilization['available_power'] = powerfeed_available / 3
+                stats.append(utilization)
+
+        return stats
+
 
 #
 # Power outlets
@@ -2923,3 +2958,10 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
 
     def get_status_class(self):
         return STATUS_CLASSES[self.status]
+
+    @property
+    def available_power(self):
+        kva = self.voltage * self.amperage * self.max_utilization
+        if self.phase == POWERFEED_PHASE_3PHASE:
+            return kva * 1.732
+        return kva

+ 43 - 0
netbox/templates/dcim/device.html

@@ -332,6 +332,49 @@
                     {% endif %}
                 </div>
             {% endif %}
+            {% if power_ports and poweroutlets %}
+                <div class="panel panel-default">
+                    <div class="panel-heading">
+                        <strong>Power Utilization</strong>
+                    </div>
+                    <table class="table table-hover panel-body">
+                        <tr>
+                            <th>Input</th>
+                            <th>Outlets</th>
+                            <th>Allocated/Max (W)</th>
+                            <th>Available (VA)</th>
+                        </tr>
+                        {% for pp in power_ports %}
+                            {% for leg in pp.get_power_stats %}
+                                <tr>
+                                    {% if leg.name %}
+                                        <td style="padding-left: 20px">{{ leg.name }}</td>
+                                    {% else %}
+                                        <td>{{ pp }}</td>
+                                    {% endif %}
+                                    <td>{{ leg.outlets|placeholder }}</td>
+                                    <td>{{ leg.allocated_draw }} / {{ leg.maximum_draw }}</td>
+                                    <td>{{ leg.available_power }}</td>
+                                </tr>
+                            {% endfor %}
+                        {% endfor %}
+                    </table>
+                    {% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
+                        <div class="panel-footer text-right noprint">
+                            {% if perms.dcim.add_consoleport %}
+                                <a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
+                                    <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
+                                </a>
+                            {% endif %}
+                            {% if perms.dcim.add_powerport %}
+                                <a href="{% url 'dcim:powerport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
+                                    <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
+                                </a>
+                            {% endif %}
+                        </div>
+                    {% endif %}
+                </div>
+            {% endif %}
             {% if request.user.is_authenticated %}
                 <div class="panel panel-default">
                     <div class="panel-heading">