mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Fixes #3849: Fix ordering of models when dumping data to JSON
This commit is contained in:
parent
7e0073d6f5
commit
e5c5a1a101
@ -14,6 +14,7 @@
|
|||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
|
||||||
* [#3589](https://github.com/netbox-community/netbox/issues/3589) - Fix validation on tagged VLANs of an interface
|
* [#3589](https://github.com/netbox-community/netbox/issues/3589) - Fix validation on tagged VLANs of an interface
|
||||||
|
* [#3849](https://github.com/netbox-community/netbox/issues/3849) - Fix ordering of models when dumping data to JSON
|
||||||
* [#3853](https://github.com/netbox-community/netbox/issues/3853) - Fix device role link on config context view
|
* [#3853](https://github.com/netbox-community/netbox/issues/3853) - Fix device role link on config context view
|
||||||
* [#3856](https://github.com/netbox-community/netbox/issues/3856) - Allow filtering VM interfaces by multiple MAC addresses
|
* [#3856](https://github.com/netbox-community/netbox/issues/3856) - Allow filtering VM interfaces by multiple MAC addresses
|
||||||
* [#3857](https://github.com/netbox-community/netbox/issues/3857) - Fix group custom links rendering
|
* [#3857](https://github.com/netbox-community/netbox/issues/3857) - Fix group custom links rendering
|
||||||
|
@ -2755,6 +2755,187 @@ class VirtualChassis(ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Power
|
||||||
|
#
|
||||||
|
|
||||||
|
class PowerPanel(ChangeLoggedModel):
|
||||||
|
"""
|
||||||
|
A distribution point for electrical power; e.g. a data center RPP.
|
||||||
|
"""
|
||||||
|
site = models.ForeignKey(
|
||||||
|
to='Site',
|
||||||
|
on_delete=models.PROTECT
|
||||||
|
)
|
||||||
|
rack_group = models.ForeignKey(
|
||||||
|
to='RackGroup',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=50
|
||||||
|
)
|
||||||
|
|
||||||
|
csv_headers = ['site', 'rack_group_name', 'name']
|
||||||
|
|
||||||
|
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 to_csv(self):
|
||||||
|
return (
|
||||||
|
self.site.name,
|
||||||
|
self.rack_group.name if self.rack_group else None,
|
||||||
|
self.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
# RackGroup must belong to assigned Site
|
||||||
|
if self.rack_group and self.rack_group.site != self.site:
|
||||||
|
raise ValidationError("Rack group {} ({}) is in a different site than {}".format(
|
||||||
|
self.rack_group, self.rack_group.site, self.site
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
)
|
||||||
|
connected_endpoint = models.OneToOneField(
|
||||||
|
to='dcim.PowerPort',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='+',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
connection_status = models.NullBooleanField(
|
||||||
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=50
|
||||||
|
)
|
||||||
|
status = models.PositiveSmallIntegerField(
|
||||||
|
choices=POWERFEED_STATUS_CHOICES,
|
||||||
|
default=POWERFEED_STATUS_ACTIVE
|
||||||
|
)
|
||||||
|
type = models.PositiveSmallIntegerField(
|
||||||
|
choices=POWERFEED_TYPE_CHOICES,
|
||||||
|
default=POWERFEED_TYPE_PRIMARY
|
||||||
|
)
|
||||||
|
supply = models.PositiveSmallIntegerField(
|
||||||
|
choices=POWERFEED_SUPPLY_CHOICES,
|
||||||
|
default=POWERFEED_SUPPLY_AC
|
||||||
|
)
|
||||||
|
phase = models.PositiveSmallIntegerField(
|
||||||
|
choices=POWERFEED_PHASE_CHOICES,
|
||||||
|
default=POWERFEED_PHASE_SINGLE
|
||||||
|
)
|
||||||
|
voltage = models.PositiveSmallIntegerField(
|
||||||
|
validators=[MinValueValidator(1)],
|
||||||
|
default=120
|
||||||
|
)
|
||||||
|
amperage = models.PositiveSmallIntegerField(
|
||||||
|
validators=[MinValueValidator(1)],
|
||||||
|
default=20
|
||||||
|
)
|
||||||
|
max_utilization = models.PositiveSmallIntegerField(
|
||||||
|
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||||
|
default=80,
|
||||||
|
help_text="Maximum permissible draw (percentage)"
|
||||||
|
)
|
||||||
|
available_power = models.PositiveSmallIntegerField(
|
||||||
|
default=0,
|
||||||
|
editable=False
|
||||||
|
)
|
||||||
|
comments = models.TextField(
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
custom_field_values = GenericRelation(
|
||||||
|
to='extras.CustomFieldValue',
|
||||||
|
content_type_field='obj_type',
|
||||||
|
object_id_field='obj_id'
|
||||||
|
)
|
||||||
|
|
||||||
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
|
csv_headers = [
|
||||||
|
'site', 'panel_name', 'rack_group', 'rack_name', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
|
||||||
|
'amperage', 'max_utilization', 'comments',
|
||||||
|
]
|
||||||
|
|
||||||
|
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 to_csv(self):
|
||||||
|
return (
|
||||||
|
self.power_panel.site.name,
|
||||||
|
self.power_panel.name,
|
||||||
|
self.rack.group.name if self.rack and self.rack.group else None,
|
||||||
|
self.rack.name if self.rack else None,
|
||||||
|
self.name,
|
||||||
|
self.get_status_display(),
|
||||||
|
self.get_type_display(),
|
||||||
|
self.get_supply_display(),
|
||||||
|
self.get_phase_display(),
|
||||||
|
self.voltage,
|
||||||
|
self.amperage,
|
||||||
|
self.max_utilization,
|
||||||
|
self.comments,
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
# 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
|
||||||
|
))
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
|
# Cache the available_power property on the instance
|
||||||
|
kva = self.voltage * self.amperage * (self.max_utilization / 100)
|
||||||
|
if self.phase == POWERFEED_PHASE_3PHASE:
|
||||||
|
self.available_power = round(kva * 1.732)
|
||||||
|
else:
|
||||||
|
self.available_power = round(kva)
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_type_class(self):
|
||||||
|
return STATUS_CLASSES[self.type]
|
||||||
|
|
||||||
|
def get_status_class(self):
|
||||||
|
return STATUS_CLASSES[self.status]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Cables
|
# Cables
|
||||||
#
|
#
|
||||||
@ -3008,184 +3189,3 @@ class Cable(ChangeLoggedModel):
|
|||||||
b_endpoint = b_path[-1][2]
|
b_endpoint = b_path[-1][2]
|
||||||
|
|
||||||
return a_endpoint, b_endpoint, path_status
|
return a_endpoint, b_endpoint, path_status
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Power
|
|
||||||
#
|
|
||||||
|
|
||||||
class PowerPanel(ChangeLoggedModel):
|
|
||||||
"""
|
|
||||||
A distribution point for electrical power; e.g. a data center RPP.
|
|
||||||
"""
|
|
||||||
site = models.ForeignKey(
|
|
||||||
to='Site',
|
|
||||||
on_delete=models.PROTECT
|
|
||||||
)
|
|
||||||
rack_group = models.ForeignKey(
|
|
||||||
to='RackGroup',
|
|
||||||
on_delete=models.PROTECT,
|
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
|
|
||||||
csv_headers = ['site', 'rack_group_name', 'name']
|
|
||||||
|
|
||||||
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 to_csv(self):
|
|
||||||
return (
|
|
||||||
self.site.name,
|
|
||||||
self.rack_group.name if self.rack_group else None,
|
|
||||||
self.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
|
|
||||||
# RackGroup must belong to assigned Site
|
|
||||||
if self.rack_group and self.rack_group.site != self.site:
|
|
||||||
raise ValidationError("Rack group {} ({}) is in a different site than {}".format(
|
|
||||||
self.rack_group, self.rack_group.site, self.site
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
)
|
|
||||||
connected_endpoint = models.OneToOneField(
|
|
||||||
to='dcim.PowerPort',
|
|
||||||
on_delete=models.SET_NULL,
|
|
||||||
related_name='+',
|
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
connection_status = models.NullBooleanField(
|
|
||||||
choices=CONNECTION_STATUS_CHOICES,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
status = models.PositiveSmallIntegerField(
|
|
||||||
choices=POWERFEED_STATUS_CHOICES,
|
|
||||||
default=POWERFEED_STATUS_ACTIVE
|
|
||||||
)
|
|
||||||
type = models.PositiveSmallIntegerField(
|
|
||||||
choices=POWERFEED_TYPE_CHOICES,
|
|
||||||
default=POWERFEED_TYPE_PRIMARY
|
|
||||||
)
|
|
||||||
supply = models.PositiveSmallIntegerField(
|
|
||||||
choices=POWERFEED_SUPPLY_CHOICES,
|
|
||||||
default=POWERFEED_SUPPLY_AC
|
|
||||||
)
|
|
||||||
phase = models.PositiveSmallIntegerField(
|
|
||||||
choices=POWERFEED_PHASE_CHOICES,
|
|
||||||
default=POWERFEED_PHASE_SINGLE
|
|
||||||
)
|
|
||||||
voltage = models.PositiveSmallIntegerField(
|
|
||||||
validators=[MinValueValidator(1)],
|
|
||||||
default=120
|
|
||||||
)
|
|
||||||
amperage = models.PositiveSmallIntegerField(
|
|
||||||
validators=[MinValueValidator(1)],
|
|
||||||
default=20
|
|
||||||
)
|
|
||||||
max_utilization = models.PositiveSmallIntegerField(
|
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
|
||||||
default=80,
|
|
||||||
help_text="Maximum permissible draw (percentage)"
|
|
||||||
)
|
|
||||||
available_power = models.PositiveSmallIntegerField(
|
|
||||||
default=0,
|
|
||||||
editable=False
|
|
||||||
)
|
|
||||||
comments = models.TextField(
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
custom_field_values = GenericRelation(
|
|
||||||
to='extras.CustomFieldValue',
|
|
||||||
content_type_field='obj_type',
|
|
||||||
object_id_field='obj_id'
|
|
||||||
)
|
|
||||||
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
|
||||||
|
|
||||||
csv_headers = [
|
|
||||||
'site', 'panel_name', 'rack_group', 'rack_name', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
|
|
||||||
'amperage', 'max_utilization', 'comments',
|
|
||||||
]
|
|
||||||
|
|
||||||
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 to_csv(self):
|
|
||||||
return (
|
|
||||||
self.power_panel.site.name,
|
|
||||||
self.power_panel.name,
|
|
||||||
self.rack.group.name if self.rack and self.rack.group else None,
|
|
||||||
self.rack.name if self.rack else None,
|
|
||||||
self.name,
|
|
||||||
self.get_status_display(),
|
|
||||||
self.get_type_display(),
|
|
||||||
self.get_supply_display(),
|
|
||||||
self.get_phase_display(),
|
|
||||||
self.voltage,
|
|
||||||
self.amperage,
|
|
||||||
self.max_utilization,
|
|
||||||
self.comments,
|
|
||||||
)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
|
|
||||||
# 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
|
|
||||||
))
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
|
|
||||||
# Cache the available_power property on the instance
|
|
||||||
kva = self.voltage * self.amperage * (self.max_utilization / 100)
|
|
||||||
if self.phase == POWERFEED_PHASE_3PHASE:
|
|
||||||
self.available_power = round(kva * 1.732)
|
|
||||||
else:
|
|
||||||
self.available_power = round(kva)
|
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_type_class(self):
|
|
||||||
return STATUS_CLASSES[self.type]
|
|
||||||
|
|
||||||
def get_status_class(self):
|
|
||||||
return STATUS_CLASSES[self.status]
|
|
||||||
|
Loading…
Reference in New Issue
Block a user