diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 037a5a765..ee17c0062 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -544,7 +544,7 @@ class VirtualChassisViewSet(ModelViewSet): # class PowerPanelViewSet(ModelViewSet): - queryset = PowerPanel.objects.all() + queryset = PowerPanel.objects.select_related('site', 'rack_group') serializer_class = serializers.PowerPanelSerializer filterset_class = filters.PowerPanelFilter @@ -553,8 +553,8 @@ class PowerPanelViewSet(ModelViewSet): # Power feeds # -class PowerFeedViewSet(ModelViewSet): - queryset = PowerFeed.objects.all() +class PowerFeedViewSet(CustomFieldModelViewSet): + queryset = PowerFeed.objects.select_related('power_panel', 'rack').prefetch_related('tags') serializer_class = serializers.PowerFeedSerializer filterset_class = filters.PowerFeedFilter diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 581ed76bf..ea535f4f2 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -1135,10 +1135,11 @@ class PowerFeedFilter(CustomFieldFilterSet): queryset=Rack.objects.all(), label='Rack (ID)', ) + tag = TagFilter() class Meta: model = PowerFeed - fields = ['name', 'status', 'type', 'supply', 'phase'] + fields = ['name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'power_factor'] def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 8fcad0bfe..ba639a646 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -3456,6 +3456,7 @@ class PowerFeedForm(BootstrapMixin, CustomFieldForm): } ) ) + comments = CommentField() tags = TagField( required=False ) @@ -3479,6 +3480,14 @@ class PowerFeedForm(BootstrapMixin, CustomFieldForm): 'phase': StaticSelect2(), } + def __init__(self, *args, **kwargs): + + super().__init__(*args, **kwargs) + + # Initialize site field + if self.instance and self.instance.power_panel: + self.initial['site'] = self.instance.power_panel.site + class PowerFeedCSVForm(forms.ModelForm): site = forms.ModelChoiceField( @@ -3573,11 +3582,11 @@ class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd } ) ) - rackgroup = forms.ModelChoiceField( - queryset=RackGroup.objects.all(), + rack = forms.ModelChoiceField( + queryset=Rack.objects.all(), required=False, widget=APISelect( - api_url="/api/dcim/rack-groups", + api_url="/api/dcim/racks", ) ) status = forms.ChoiceField( @@ -3669,3 +3678,12 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldFilterForm): required=False, widget=StaticSelect2() ) + voltage = forms.IntegerField( + required=False + ) + amperage = forms.IntegerField( + required=False + ) + power_factor = forms.IntegerField( + required=False + ) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index cdaf247c8..020d30618 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2853,6 +2853,14 @@ class PowerPanel(ChangeLoggedModel): 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): """ @@ -2953,6 +2961,14 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel): 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 get_type_class(self): return STATUS_CLASSES[self.type] diff --git a/netbox/templates/dcim/powerfeed.html b/netbox/templates/dcim/powerfeed.html index 0686afd20..f2c16927b 100644 --- a/netbox/templates/dcim/powerfeed.html +++ b/netbox/templates/dcim/powerfeed.html @@ -8,6 +8,7 @@