mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-21 03:27:21 -06:00
Add associatiton from power outlet to power port/phase
This commit is contained in:
parent
0ddd71fc36
commit
3d5f85c0ca
@ -215,10 +215,16 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
|
||||
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
power_port = PowerPortTemplateSerializer()
|
||||
feed_leg = ChoiceField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutletTemplate
|
||||
fields = ['id', 'device_type', 'name']
|
||||
fields = ['id', 'device_type', 'name', 'power_port', 'feed_leg']
|
||||
|
||||
|
||||
class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
||||
@ -372,14 +378,24 @@ class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
|
||||
class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
power_port = NestedPowerPortSerializer()
|
||||
feed_leg = ChoiceField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
cable = NestedCableSerializer(
|
||||
read_only=True
|
||||
)
|
||||
tags = TagListSerializerField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
|
||||
'cable', 'tags',
|
||||
'id', 'device', 'name', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
|
||||
'connected_endpoint', 'connection_status', 'cable', 'tags',
|
||||
]
|
||||
|
||||
|
||||
|
@ -44,6 +44,8 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
|
||||
(FrontPortTemplate, ['type']),
|
||||
(Interface, ['form_factor', 'mode']),
|
||||
(InterfaceTemplate, ['form_factor']),
|
||||
(PowerOutlet, ['feed_leg']),
|
||||
(PowerOutletTemplate, ['feed_leg']),
|
||||
(PowerPort, ['connection_status']),
|
||||
(Rack, ['outer_unit', 'status', 'type', 'width']),
|
||||
(RearPort, ['type']),
|
||||
|
@ -475,3 +475,11 @@ POWERFEED_STATUS_CHOICES = (
|
||||
(POWERFEED_STATUS_PLANNED, 'Planned'),
|
||||
(POWERFEED_STATUS_FAILED, 'Failed'),
|
||||
)
|
||||
POWERFEED_LEG_A = 1
|
||||
POWERFEED_LEG_B = 2
|
||||
POWERFEED_LEG_C = 3
|
||||
POWERFEED_LEG_CHOICES = (
|
||||
(POWERFEED_LEG_A, 'A'),
|
||||
(POWERFEED_LEG_B, 'B'),
|
||||
(POWERFEED_LEG_C, 'C'),
|
||||
)
|
||||
|
@ -977,16 +977,29 @@ class PowerPortTemplateCreateForm(ComponentForm):
|
||||
|
||||
|
||||
class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
power_port = forms.ModelChoiceField(
|
||||
queryset=PowerPortTemplate.objects.all(),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutletTemplate
|
||||
fields = [
|
||||
'device_type', 'name',
|
||||
'device_type', 'name', 'power_port', 'feed_leg',
|
||||
]
|
||||
widgets = {
|
||||
'device_type': forms.HiddenInput(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit power_port choices to current DeviceType
|
||||
self.fields['power_port'].queryset = PowerPortTemplate.objects.filter(
|
||||
device_type=self.parent
|
||||
)
|
||||
|
||||
|
||||
class PowerOutletTemplateCreateForm(ComponentForm):
|
||||
name_pattern = ExpandableNameField(
|
||||
@ -1972,6 +1985,10 @@ class PowerPortCreateForm(ComponentForm):
|
||||
#
|
||||
|
||||
class PowerOutletForm(BootstrapMixin, forms.ModelForm):
|
||||
power_port = forms.ModelChoiceField(
|
||||
queryset=PowerPort.objects.all(),
|
||||
required=False
|
||||
)
|
||||
tags = TagField(
|
||||
required=False
|
||||
)
|
||||
@ -1979,12 +1996,20 @@ class PowerOutletForm(BootstrapMixin, forms.ModelForm):
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'device', 'name', 'description', 'tags',
|
||||
'device', 'name', 'power_port', 'feed_leg', 'description', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'device': forms.HiddenInput(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit power_port choices to the local device
|
||||
self.fields['power_port'].queryset = PowerPort.objects.filter(
|
||||
device=self.instance.device
|
||||
)
|
||||
|
||||
|
||||
class PowerOutletCreateForm(ComponentForm):
|
||||
name_pattern = ExpandableNameField(
|
||||
@ -2004,6 +2029,10 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
queryset=PowerOutlet.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
feed_leg = forms.ChoiceField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
required=False,
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=100,
|
||||
required=False
|
||||
|
@ -1,5 +1,3 @@
|
||||
# Generated by Django 2.1.7 on 2019-03-21 20:59
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@ -112,4 +110,24 @@ class Migration(migrations.Migration):
|
||||
name='powerfeed',
|
||||
unique_together={('power_panel', 'name')},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='feed_leg',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='power_port',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlets', to='dcim.PowerPort'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='feed_leg',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='power_port',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlet_templates', to='dcim.PowerPortTemplate'),
|
||||
),
|
||||
]
|
||||
|
@ -1088,6 +1088,19 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
power_port = models.ForeignKey(
|
||||
to='dcim.PowerPortTemplate',
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='poweroutlet_templates'
|
||||
)
|
||||
feed_leg = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text="Phase (for three-phase feeds)"
|
||||
)
|
||||
|
||||
objects = DeviceComponentManager()
|
||||
|
||||
@ -1098,6 +1111,14 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate power port assignment
|
||||
if self.power_port and self.power_port.device_type != self.device_type:
|
||||
raise ValidationError(
|
||||
"Parent power port ({}) must belong to the same device type".format(self.power_port)
|
||||
)
|
||||
|
||||
|
||||
class InterfaceTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
@ -1934,6 +1955,19 @@ class PowerOutlet(CableTermination, ComponentModel):
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
power_port = models.ForeignKey(
|
||||
to='dcim.PowerPort',
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='poweroutlets'
|
||||
)
|
||||
feed_leg = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text="Phase (for three-phase feeds)"
|
||||
)
|
||||
connection_status = models.NullBooleanField(
|
||||
choices=CONNECTION_STATUS_CHOICES,
|
||||
blank=True
|
||||
@ -1942,7 +1976,7 @@ class PowerOutlet(CableTermination, ComponentModel):
|
||||
objects = DeviceComponentManager()
|
||||
tags = TaggableManager(through=TaggedItem)
|
||||
|
||||
csv_headers = ['device', 'name', 'description']
|
||||
csv_headers = ['device', 'name', 'power_port', 'feed_leg', 'description']
|
||||
|
||||
class Meta:
|
||||
unique_together = ['device', 'name']
|
||||
@ -1957,9 +1991,19 @@ class PowerOutlet(CableTermination, ComponentModel):
|
||||
return (
|
||||
self.device.identifier,
|
||||
self.name,
|
||||
self.power_port.name if self.power_port else None,
|
||||
self.get_feed_leg_display(),
|
||||
self.description,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
|
||||
# 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)
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Interfaces
|
||||
|
@ -627,6 +627,7 @@
|
||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||
{% endif %}
|
||||
<th>Name</th>
|
||||
<th>Input/Leg</th>
|
||||
<th>Description</th>
|
||||
<th>Cable</th>
|
||||
<th colspan="3">Connection</th>
|
||||
|
@ -14,6 +14,15 @@
|
||||
<i class="fa fa-fw fa-bolt"></i> {{ po }}
|
||||
</td>
|
||||
|
||||
{# Power port #}
|
||||
<td>
|
||||
{% if po.power_port %}
|
||||
{{ po.power_port }}{% if po.feed_leg %} / {{ po.get_feed_leg_display }}{% endif %}
|
||||
{% else %}
|
||||
<span class="text-warning">None</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Description #}
|
||||
<td>
|
||||
{{ po.description|placeholder }}
|
||||
|
Loading…
Reference in New Issue
Block a user