diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index eb1d452fa..e1d98a2d4 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1962,48 +1962,48 @@ class PowerPort(CableTermination, ComponentModel): "Connected endpoint must be a PowerOutlet or PowerFeed, not {}.".format(type(value)) ) - def get_power_stats(self): + def get_power_draw(self): """ - Return utilization statistics for this PowerPort. + Return the allocated and maximum power draw (in VA) and child PowerOutlet count for this PowerPort. """ - feed = self._connected_powerfeed - if not feed or not self.poweroutlets.count(): - return None + # Calculate aggregate draw of all child power outlets if no numbers have been defined manually + if self.allocated_draw is None and self.maximum_draw is None: + 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_total=Sum('maximum_draw'), + allocated_draw_total=Sum('allocated_draw'), + ) + ret = { + 'allocated': utilization['allocated_draw_total'] or 0, + 'maximum': utilization['maximum_draw_total'] or 0, + 'outlet_count': len(outlet_ids), + 'legs': [], + } - stats = [] - powerfeed_available = self._connected_powerfeed.available_power + # Calculate per-leg aggregates for three-phase feeds + if self._connected_powerfeed and self._connected_powerfeed.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_total=Sum('maximum_draw'), + allocated_draw_total=Sum('allocated_draw'), + ) + ret['legs'].append({ + 'name': leg_name, + 'allocated': utilization['allocated_draw_total'] or 0, + 'maximum': utilization['maximum_draw_total'] or 0, + 'outlet_count': len(outlet_ids), + }) - 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_total=Sum('maximum_draw'), - allocated_draw_total=Sum('allocated_draw'), - ) - utilization['outlets'] = len(outlet_ids) - utilization['available_power'] = powerfeed_available - allocated_utilization = int( - float(utilization['allocated_draw_total'] or 0) / powerfeed_available * 100 - ) - utilization['allocated_utilization'] = allocated_utilization - stats.append(utilization) + return ret - # 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_total=Sum('maximum_draw'), - allocated_draw_total=Sum('allocated_draw'), - ) - utilization['name'] = 'Leg {}'.format(leg_name) - utilization['outlets'] = len(outlet_ids) - utilization['available_power'] = round(powerfeed_available / 3) - allocated_utilization = int( - float(utilization['allocated_draw_total'] or 0) / powerfeed_available * 100 - ) - utilization['allocated_utilization'] = allocated_utilization - stats.append(utilization) - - return stats + # Default to administratively defined values + return { + 'allocated': self.allocated_draw or 0, + 'maximum': self.maximum_draw or 0, + 'outlet_count': PowerOutlet.objects.filter(power_port=self).count(), + 'legs': [], + } # @@ -3037,14 +3037,3 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel): def get_status_class(self): return STATUS_CLASSES[self.status] - - def get_power_stats(self): - """ - Return power utilization statistics - """ - power_port = self.connected_endpoint - if not power_port: - # Nothing is connected to the feed so it is not being utilized - return None - - return power_port.get_power_stats() diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index bbd30ac0b..aafb35a0f 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -299,8 +299,16 @@ class RackDetailTable(RackTable): template_code=RACK_DEVICE_COUNT, verbose_name='Devices' ) - get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization') - get_power_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Power Utilization') + get_utilization = tables.TemplateColumn( + template_code=UTILIZATION_GRAPH, + orderable=False, + verbose_name='Space' + ) + get_power_utilization = tables.TemplateColumn( + template_code=UTILIZATION_GRAPH, + orderable=False, + verbose_name='Power' + ) class Meta(RackTable.Meta): fields = ( diff --git a/netbox/project-static/css/base.css b/netbox/project-static/css/base.css index e87811bc1..fcee05e12 100644 --- a/netbox/project-static/css/base.css +++ b/netbox/project-static/css/base.css @@ -586,6 +586,7 @@ ul.nav-tabs, ul.nav-pills { /* Fix progress bar margin inside table cells */ td .progress { margin-bottom: 0; + min-width: 100px; } textarea { font-family: Consolas, Lucida Console, monospace; diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 5a0515b06..5f0bbdfc0 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -349,24 +349,36 @@
Outlets | -Allocated/Max (W) | -Available (VA) | -Utilization (Allocated) | -
---|---|---|---|
{{ leg.outlets|placeholder }} | -{{ leg.allocated_draw_total }} / {{ leg.maximum_draw_total }} | -{{ leg.available_power }} | -{% utilization_graph leg.allocated_utilization %} | -