Explorar o código

#22447: Pre-release QA (#22908)

* Fixes #22447: Pre-release QA

Add the missing `cooling_outflow` GraphQL filter on CoolingIntake, so an
intake can be filtered by its upstream outflow. CoolingOutflow already
exposes the reverse `cooling_intake` filter and the REST filterset already
carries `cooling_outflow_id`; the GraphQL intake filter was the only side
missing it.

Correct the CoolingIntake docstring, which referenced a direct CoolingFeed
relationship that does not exist. The serving feed is derived from the
device's rack, not stored on the intake.

* Fixes #22447: Pre-release QA (filter form + table parity)

Address the same-class gaps surfaced in review, all mirror images of the
intake/outflow filter parity already fixed:

Add the `cooling_intake_id` filter to CoolingOutflowFilterForm and
CoolingOutflowTemplateFilterForm. The underlying filtersets already carried
`cooling_intake_id` and GraphQL supported it, but the list-view filter panel
did not expose it, so an outflow could not be filtered by its downstream
intake from the UI.

Add `cooling_outflow` to the default columns on CoolingIntakeTable and
DeviceCoolingIntakeTable. The outflow tables already default-show
`cooling_intake`; the intake tables hid the reverse, so the same relationship
displayed inconsistently between the two sides.

Note in the CoolingIntake docstring why CoolingIntakeTemplate has no
upstream-outflow field: an intake's outflow normally lives on a different
device (a CDU), which a device-type template cannot express.
Jason Novinger hai 1 semana
pai
achega
feffda99d7

+ 12 - 2
netbox/dcim/forms/filtersets.py

@@ -1940,7 +1940,7 @@ class CoolingOutflowFilterForm(DeviceComponentFilterForm):
     fieldsets = (
         FieldSet('q', 'filter_id', 'tag'),
         FieldSet(
-            'name', 'label', 'type', 'diameter', 'diameter_unit', name=_('Attributes')
+            'name', 'label', 'type', 'diameter', 'diameter_unit', 'cooling_intake_id', name=_('Attributes')
         ),
         FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
         FieldSet(
@@ -1963,6 +1963,11 @@ class CoolingOutflowFilterForm(DeviceComponentFilterForm):
         choices=DiameterUnitChoices,
         required=False
     )
+    cooling_intake_id = DynamicModelMultipleChoiceField(
+        label=_('Cooling intake'),
+        queryset=CoolingIntake.objects.all(),
+        required=False
+    )
     tag = TagFilterField(model)
 
 
@@ -1970,7 +1975,7 @@ class CoolingOutflowTemplateFilterForm(ModularDeviceComponentTemplateFilterForm)
     model = CoolingOutflowTemplate
     fieldsets = (
         FieldSet('q', 'filter_id', 'tag'),
-        FieldSet('name', 'label', 'type', 'diameter', 'diameter_unit', name=_('Attributes')),
+        FieldSet('name', 'label', 'type', 'diameter', 'diameter_unit', 'cooling_intake_id', name=_('Attributes')),
         FieldSet('device_type_id', 'module_type_id', name=_('Device')),
     )
     type = forms.MultipleChoiceField(
@@ -1987,6 +1992,11 @@ class CoolingOutflowTemplateFilterForm(ModularDeviceComponentTemplateFilterForm)
         choices=DiameterUnitChoices,
         required=False
     )
+    cooling_intake_id = DynamicModelMultipleChoiceField(
+        label=_('Cooling intake'),
+        queryset=CoolingIntakeTemplate.objects.all(),
+        required=False
+    )
 
 
 class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):

+ 4 - 0
netbox/dcim/graphql/filters.py

@@ -1068,6 +1068,10 @@ class CoolingIntakeFilter(ModularComponentFilterMixin, NetBoxModelFilter):
     max_flow_unit: BaseFilterLookup[
         Annotated['FlowRateUnitEnum', strawberry.lazy('dcim.graphql.enums')]
     ] | None = strawberry_django.filter_field()
+    cooling_outflow: Annotated['CoolingOutflowFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
+        strawberry_django.filter_field()
+    )
+    cooling_outflow_id: ID | None = strawberry_django.filter_field()
 
 
 @register_filter(models.CoolingIntakeTemplate, lookups=True)

+ 6 - 1
netbox/dcim/models/device_components.py

@@ -700,7 +700,12 @@ class CoolingIntake(
 ):
     """
     A coolant intake port within a Device (e.g. a server cold-plate inlet or CDU intake). A
-    CoolingIntake is supplied by an upstream CoolingOutflow or CoolingFeed.
+    CoolingIntake is supplied by an upstream CoolingOutflow. The serving CoolingFeed is
+    derived from the Device's Rack rather than referenced directly.
+
+    Unlike CoolingOutflow (whose parent intake is on the same Device and can therefore be
+    templated), an intake's upstream outflow typically lives on a different Device (e.g. a
+    CDU), so there is deliberately no upstream-outflow field on CoolingIntakeTemplate.
     """
     type = models.CharField(
         verbose_name=_('type'),

+ 2 - 2
netbox/dcim/tables/cooling.py

@@ -166,7 +166,7 @@ class CoolingIntakeTable(ModularDeviceComponentTable):
         )
         default_columns = (
             'pk', 'name', 'device', 'label', 'type', 'diameter', 'max_flow',
-            'description',
+            'cooling_outflow', 'description',
         )
 
 
@@ -281,7 +281,7 @@ class DeviceCoolingIntakeTable(CoolingIntakeTable):
         )
         default_columns = (
             'pk', 'name', 'label', 'type', 'diameter', 'max_flow',
-            'description',
+            'cooling_outflow', 'description',
         )