1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

178 lines
5.0 KiB
Python
Raw Normal View History

from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.urls import reverse
from dcim.choices import *
from dcim.constants import *
from netbox.models import PrimaryModel
from utilities.validators import ExclusionValidator
2021-10-13 14:04:53 -04:00
from .device_components import LinkTermination, PathEndpoint
__all__ = (
'PowerFeed',
'PowerPanel',
)
#
# Power
#
class PowerPanel(PrimaryModel):
"""
A distribution point for electrical power; e.g. a data center RPP.
"""
site = models.ForeignKey(
to='Site',
on_delete=models.PROTECT
)
location = models.ForeignKey(
to='dcim.Location',
on_delete=models.PROTECT,
blank=True,
null=True
)
name = models.CharField(
max_length=100
)
2021-10-18 15:09:57 -04:00
# Generic relations
contacts = GenericRelation(
to='tenancy.ContactAssignment'
)
images = GenericRelation(
to='extras.ImageAttachment'
)
class Meta:
ordering = ['site', 'name']
unique_together = ['site', 'name']
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('dcim:powerpanel', args=[self.pk])
def clean(self):
super().clean()
# Location must belong to assigned Site
if self.location and self.location.site != self.site:
raise ValidationError(
f"Location {self.location} ({self.location.site}) is in a different site than {self.site}"
)
2021-10-13 14:04:53 -04:00
class PowerFeed(PrimaryModel, PathEndpoint, LinkTermination):
"""
An electrical circuit delivered from a PowerPanel.
"""
power_panel = models.ForeignKey(
to='PowerPanel',
on_delete=models.PROTECT,
related_name='powerfeeds'
)
rack = models.ForeignKey(
to='Rack',
on_delete=models.PROTECT,
blank=True,
null=True
)
name = models.CharField(
max_length=100
)
status = models.CharField(
max_length=50,
choices=PowerFeedStatusChoices,
default=PowerFeedStatusChoices.STATUS_ACTIVE
)
type = models.CharField(
max_length=50,
choices=PowerFeedTypeChoices,
default=PowerFeedTypeChoices.TYPE_PRIMARY
)
supply = models.CharField(
max_length=50,
choices=PowerFeedSupplyChoices,
default=PowerFeedSupplyChoices.SUPPLY_AC
)
phase = models.CharField(
max_length=50,
choices=PowerFeedPhaseChoices,
default=PowerFeedPhaseChoices.PHASE_SINGLE
)
voltage = models.SmallIntegerField(
default=POWERFEED_VOLTAGE_DEFAULT,
validators=[ExclusionValidator([0])]
)
amperage = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1)],
default=POWERFEED_AMPERAGE_DEFAULT
)
max_utilization = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(100)],
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
help_text="Maximum permissible draw (percentage)"
)
available_power = models.PositiveIntegerField(
default=0,
editable=False
)
comments = models.TextField(
blank=True
)
clone_fields = [
'power_panel', 'rack', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'available_power',
]
class Meta:
ordering = ['power_panel', 'name']
unique_together = ['power_panel', 'name']
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('dcim:powerfeed', args=[self.pk])
def clean(self):
super().clean()
# Rack must belong to same Site as PowerPanel
if self.rack and self.rack.site != self.power_panel.site:
raise ValidationError("Rack {} ({}) and power panel {} ({}) are in different sites".format(
self.rack, self.rack.site, self.power_panel, self.power_panel.site
))
# AC voltage cannot be negative
if self.voltage < 0 and self.supply == PowerFeedSupplyChoices.SUPPLY_AC:
raise ValidationError({
"voltage": "Voltage cannot be negative for AC supply"
})
def save(self, *args, **kwargs):
# Cache the available_power property on the instance
kva = abs(self.voltage) * self.amperage * (self.max_utilization / 100)
if self.phase == PowerFeedPhaseChoices.PHASE_3PHASE:
self.available_power = round(kva * 1.732)
else:
self.available_power = round(kva)
super().save(*args, **kwargs)
@property
def parent_object(self):
return self.power_panel
def get_type_class(self):
return PowerFeedTypeChoices.colors.get(self.type)
def get_status_class(self):
return PowerFeedStatusChoices.colors.get(self.status, 'secondary')