diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index fcf3252cf..d1f9f59f2 100644 --- a/netbox/dcim/models.py +++ b/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 diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 34811c4c0..b9f283837 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -332,6 +332,49 @@ {% endif %} {% endif %} + {% if power_ports and poweroutlets %} +
Input | +Outlets | +Allocated/Max (W) | +Available (VA) | +|
---|---|---|---|---|
{{ leg.name }} | + {% else %} +{{ pp }} | + {% endif %} +{{ leg.outlets|placeholder }} | +{{ leg.allocated_draw }} / {{ leg.maximum_draw }} | +{{ leg.available_power }} | +