mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 04:02:52 -06:00
Update power utilization calculations for new cabling model
This commit is contained in:
parent
25ed3390cb
commit
fcd1daaf79
@ -3,7 +3,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Sum
|
||||
from django.db.models import Q, Sum
|
||||
from django.urls import reverse
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
|
||||
@ -156,6 +156,12 @@ class LinkTermination(models.Model):
|
||||
def parent_object(self):
|
||||
raise NotImplementedError(f"{self.__class__.__name__} models must declare a parent_object property")
|
||||
|
||||
@property
|
||||
def opposite_cable_end(self):
|
||||
if not self.cable_end:
|
||||
return None
|
||||
return CableEndChoices.SIDE_A if self.cable_end == CableEndChoices.SIDE_B else CableEndChoices.SIDE_B
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
"""
|
||||
@ -333,36 +339,49 @@ class PowerPort(ModularComponentModel, LinkTermination, PathEndpoint):
|
||||
'allocated_draw': f"Allocated draw cannot exceed the maximum draw ({self.maximum_draw}W)."
|
||||
})
|
||||
|
||||
def get_downstream_powerports(self, leg=None):
|
||||
"""
|
||||
Return a queryset of all PowerPorts connected via cable to a child PowerOutlet.
|
||||
"""
|
||||
poweroutlets = self.poweroutlets.filter(cable__isnull=False)
|
||||
if leg:
|
||||
poweroutlets = poweroutlets.filter(feed_leg=leg)
|
||||
if not poweroutlets:
|
||||
return PowerPort.objects.none()
|
||||
|
||||
q = Q()
|
||||
for poweroutlet in poweroutlets:
|
||||
q |= Q(
|
||||
cable=poweroutlet.cable,
|
||||
cable_end=poweroutlet.opposite_cable_end
|
||||
)
|
||||
|
||||
return PowerPort.objects.filter(q)
|
||||
|
||||
def get_power_draw(self):
|
||||
"""
|
||||
Return the allocated and maximum power draw (in VA) and child PowerOutlet count for this PowerPort.
|
||||
"""
|
||||
from dcim.models import PowerFeed
|
||||
|
||||
# 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:
|
||||
poweroutlet_ct = ContentType.objects.get_for_model(PowerOutlet)
|
||||
outlet_ids = PowerOutlet.objects.filter(power_port=self).values_list('pk', flat=True)
|
||||
utilization = PowerPort.objects.filter(
|
||||
_link_peer_type=poweroutlet_ct,
|
||||
_link_peer_id__in=outlet_ids
|
||||
).aggregate(
|
||||
utilization = self.get_downstream_powerports().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),
|
||||
'outlet_count': self.poweroutlets.count(),
|
||||
'legs': [],
|
||||
}
|
||||
|
||||
# Calculate per-leg aggregates for three-phase feeds
|
||||
if getattr(self._link_peer, 'phase', None) == PowerFeedPhaseChoices.PHASE_3PHASE:
|
||||
# Calculate per-leg aggregates for three-phase power feeds
|
||||
if len(self.link_peers) == 1 and isinstance(self.link_peers[0], PowerFeed) and \
|
||||
self.link_peers[0].phase == PowerFeedPhaseChoices.PHASE_3PHASE:
|
||||
for leg, leg_name in PowerOutletFeedLegChoices:
|
||||
outlet_ids = PowerOutlet.objects.filter(power_port=self, feed_leg=leg).values_list('pk', flat=True)
|
||||
utilization = PowerPort.objects.filter(
|
||||
_link_peer_type=poweroutlet_ct,
|
||||
_link_peer_id__in=outlet_ids
|
||||
).aggregate(
|
||||
utilization = self.get_downstream_powerports(leg=leg).aggregate(
|
||||
maximum_draw_total=Sum('maximum_draw'),
|
||||
allocated_draw_total=Sum('allocated_draw'),
|
||||
)
|
||||
@ -370,7 +389,7 @@ class PowerPort(ModularComponentModel, LinkTermination, PathEndpoint):
|
||||
'name': leg_name,
|
||||
'allocated': utilization['allocated_draw_total'] or 0,
|
||||
'maximum': utilization['maximum_draw_total'] or 0,
|
||||
'outlet_count': len(outlet_ids),
|
||||
'outlet_count': self.poweroutlets.filter(feed_leg=leg).count(),
|
||||
})
|
||||
|
||||
return ret
|
||||
@ -379,7 +398,7 @@ class PowerPort(ModularComponentModel, LinkTermination, PathEndpoint):
|
||||
return {
|
||||
'allocated': self.allocated_draw or 0,
|
||||
'maximum': self.maximum_draw or 0,
|
||||
'outlet_count': PowerOutlet.objects.filter(power_port=self).count(),
|
||||
'outlet_count': self.poweroutlets.count(),
|
||||
'legs': [],
|
||||
}
|
||||
|
||||
@ -422,9 +441,7 @@ class PowerOutlet(ModularComponentModel, LinkTermination, PathEndpoint):
|
||||
|
||||
# Validate power port assignment
|
||||
if self.power_port and self.power_port.device != self.device:
|
||||
raise ValidationError(
|
||||
"Parent power port ({}) must belong to the same device".format(self.power_port)
|
||||
)
|
||||
raise ValidationError(f"Parent power port ({self.power_port}) must belong to the same device")
|
||||
|
||||
|
||||
#
|
||||
|
@ -429,20 +429,22 @@ class Rack(NetBoxModel):
|
||||
"""
|
||||
powerfeeds = PowerFeed.objects.filter(rack=self)
|
||||
available_power_total = sum(pf.available_power for pf in powerfeeds)
|
||||
print(f'available_power_total: {available_power_total}')
|
||||
if not available_power_total:
|
||||
return 0
|
||||
|
||||
pf_powerports = PowerPort.objects.filter(
|
||||
_link_peer_type=ContentType.objects.get_for_model(PowerFeed),
|
||||
_link_peer_id__in=powerfeeds.values_list('id', flat=True)
|
||||
)
|
||||
poweroutlets = PowerOutlet.objects.filter(power_port_id__in=pf_powerports)
|
||||
allocated_draw_total = PowerPort.objects.filter(
|
||||
_link_peer_type=ContentType.objects.get_for_model(PowerOutlet),
|
||||
_link_peer_id__in=poweroutlets.values_list('id', flat=True)
|
||||
).aggregate(Sum('allocated_draw'))['allocated_draw__sum'] or 0
|
||||
powerports = []
|
||||
for powerfeed in powerfeeds:
|
||||
powerports.extend([
|
||||
peer for peer in powerfeed.link_peers if isinstance(peer, PowerPort)
|
||||
])
|
||||
|
||||
return int(allocated_draw_total / available_power_total * 100)
|
||||
allocated_draw = 0
|
||||
for powerport in powerports:
|
||||
allocated_draw += powerport.get_power_draw()['allocated']
|
||||
print(f'allocated_draw: {allocated_draw}')
|
||||
|
||||
return int(allocated_draw / available_power_total * 100)
|
||||
|
||||
|
||||
class RackReservation(NetBoxModel):
|
||||
|
Loading…
Reference in New Issue
Block a user