diff --git a/netbox/dcim/api/serializers_/devicetype_components.py b/netbox/dcim/api/serializers_/devicetype_components.py index 8d4403d2d9..5f2d4b36aa 100644 --- a/netbox/dcim/api/serializers_/devicetype_components.py +++ b/netbox/dcim/api/serializers_/devicetype_components.py @@ -155,7 +155,7 @@ class Meta: model = PowerOutletTemplate fields = [ 'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', - 'power_port', 'feed_leg', 'description', 'created', 'last_updated', + 'color', 'power_port', 'feed_leg', 'description', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 37a0d99a2c..f9dd771cdf 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -842,7 +842,7 @@ class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceType class Meta: model = PowerOutletTemplate - fields = ('id', 'name', 'label', 'type', 'feed_leg', 'description') + fields = ('id', 'name', 'label', 'type', 'color', 'feed_leg', 'description') class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index 0f55506662..653e342498 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -1163,6 +1163,10 @@ class PowerOutletTemplateBulkEditForm(ComponentTemplateBulkEditForm): choices=add_blank_choice(PowerOutletTypeChoices), required=False ) + color = ColorField( + label=_('Color'), + required=False + ) power_port = forms.ModelChoiceField( label=_('Power port'), queryset=PowerPortTemplate.objects.all(), diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 32ea2d2636..7501dac0c8 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1092,14 +1092,14 @@ class PowerOutletTemplateForm(ModularComponentTemplateForm): FieldSet('device_type', name=_('Device Type')), FieldSet('module_type', name=_('Module Type')), ), - 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', + 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', ), ) class Meta: model = PowerOutletTemplate fields = [ - 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', + 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', ] diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 0cd5e8fd11..d29d5fa05a 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -673,6 +673,7 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin ) class PowerOutletTemplateType(ModularComponentTemplateType): power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None + color: str @strawberry_django.type( diff --git a/netbox/dcim/migrations/0216_poweroutlettemplate_color.py b/netbox/dcim/migrations/0216_poweroutlettemplate_color.py new file mode 100644 index 0000000000..f0c0762e92 --- /dev/null +++ b/netbox/dcim/migrations/0216_poweroutlettemplate_color.py @@ -0,0 +1,17 @@ +import utilities.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0215_rackreservation_status'), + ] + + operations = [ + migrations.AddField( + model_name='poweroutlettemplate', + name='color', + field=utilities.fields.ColorField(blank=True, max_length=6), + ), + ] diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index e0b05b388a..04c02f84fe 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -338,6 +338,10 @@ class PowerOutletTemplate(ModularComponentTemplateModel): blank=True, null=True ) + color = ColorField( + verbose_name=_('color'), + blank=True + ) power_port = models.ForeignKey( to='dcim.PowerPortTemplate', on_delete=models.SET_NULL, @@ -388,6 +392,7 @@ def instantiate(self, **kwargs): name=self.resolve_name(kwargs.get('module')), label=self.resolve_label(kwargs.get('module')), type=self.type, + color=self.color, power_port=power_port, feed_leg=self.feed_leg, **kwargs @@ -398,6 +403,7 @@ def to_yaml(self): return { 'name': self.name, 'type': self.type, + 'color': self.color, 'power_port': self.power_port.name if self.power_port else None, 'feed_leg': self.feed_leg, 'label': self.label, diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index 91f9f3b479..713d3f886a 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -211,6 +211,9 @@ class Meta(ComponentTemplateTable.Meta): class PowerOutletTemplateTable(ComponentTemplateTable): + color = columns.ColorColumn( + verbose_name=_('Color'), + ) actions = columns.ActionsColumn( actions=('edit', 'delete'), extra_buttons=MODULAR_COMPONENT_TEMPLATE_BUTTONS @@ -218,7 +221,7 @@ class PowerOutletTemplateTable(ComponentTemplateTable): class Meta(ComponentTemplateTable.Meta): model = models.PowerOutletTemplate - fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'actions') + fields = ('pk', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', 'actions') empty_text = "None" diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index c05d07ab03..3ab34361ec 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -1919,18 +1919,21 @@ def setUpTestData(cls): device_type=device_types[0], name='Power Outlet 1', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, + color=ColorChoices.COLOR_RED, description='foobar1' ), PowerOutletTemplate( device_type=device_types[1], name='Power Outlet 2', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B, + color=ColorChoices.COLOR_GREEN, description='foobar2' ), PowerOutletTemplate( device_type=device_types[2], name='Power Outlet 3', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C, + color=ColorChoices.COLOR_BLUE, description='foobar3' ), )) @@ -1943,6 +1946,10 @@ def test_feed_leg(self): params = {'feed_leg': [PowerOutletFeedLegChoices.FEED_LEG_A, PowerOutletFeedLegChoices.FEED_LEG_B]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_color(self): + params = {'color': [ColorChoices.COLOR_RED, ColorChoices.COLOR_GREEN]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class InterfaceTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ChangeLoggedFilterSetTests): queryset = InterfaceTemplate.objects.all()