diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 08685a2b7..2bec542ed 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -35,6 +35,7 @@ CustomValidator can also be subclassed to enforce more complex logic by overridi * [#2434](https://github.com/netbox-community/netbox/issues/2434) - Add option to assign IP address upon creating a new interface * [#3665](https://github.com/netbox-community/netbox/issues/3665) - Enable rendering export templates via REST API +* [#3682](https://github.com/netbox-community/netbox/issues/3682) - Add `color` field to front and rear ports * [#4609](https://github.com/netbox-community/netbox/issues/4609) - Allow marking prefixes as fully utilized * [#5806](https://github.com/netbox-community/netbox/issues/5806) - Add kilometer and mile as choices for cable length unit * [#6154](https://github.com/netbox-community/netbox/issues/6154) - Allow decimal values for cable lengths @@ -54,8 +55,16 @@ CustomValidator can also be subclassed to enforce more complex logic by overridi * Removed the `display_name` attribute (use `display` instead) * dcim.DeviceType * Removed the `display_name` attribute (use `display` instead) +* dcim.FrontPort + * Added `color` field +* dcim.FrontPortTemplate + * Added `color` field * dcim.Rack * Removed the `display_name` attribute (use `display` instead) +* dcim.RearPort + * Added `color` field +* dcim.RearPortTemplate + * Added `color` field * dcim.Site * `latitude` and `longitude` are now decimal fields rather than strings * extras.ContentType diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index d2ecad608..f41d5eeb4 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -384,8 +384,8 @@ class RearPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = RearPortTemplate fields = [ - 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'positions', 'description', 'created', - 'last_updated', + 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description', + 'created', 'last_updated', ] @@ -398,7 +398,7 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = FrontPortTemplate fields = [ - 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'rear_port', 'rear_port_position', + 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', 'created', 'last_updated', ] @@ -665,8 +665,9 @@ class RearPortSerializer(PrimaryModelSerializer, CableTerminationSerializer): class Meta: model = RearPort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', - 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', + 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'positions', 'description', + 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', + 'last_updated', '_occupied', ] @@ -691,9 +692,9 @@ class FrontPortSerializer(PrimaryModelSerializer, CableTerminationSerializer): class Meta: model = FrontPort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', - 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', - 'last_updated', '_occupied', + 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', + 'description', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', + 'created', 'last_updated', '_occupied', ] diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index b04c14ba9..079e43007 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -538,7 +538,7 @@ class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponent class Meta: model = FrontPortTemplate - fields = ['id', 'name', 'type'] + fields = ['id', 'name', 'type', 'color'] class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): @@ -549,7 +549,7 @@ class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentF class Meta: model = RearPortTemplate - fields = ['id', 'name', 'type', 'positions'] + fields = ['id', 'name', 'type', 'color', 'positions'] class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): @@ -1027,7 +1027,7 @@ class FrontPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableT class Meta: model = FrontPort - fields = ['id', 'name', 'label', 'type', 'description'] + fields = ['id', 'name', 'label', 'type', 'color', 'description'] class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet): @@ -1038,7 +1038,7 @@ class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTe class Meta: model = RearPort - fields = ['id', 'name', 'label', 'type', 'positions', 'description'] + fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description'] class DeviceBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet): diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index ea0d7eeac..d8ca1a5bd 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1610,7 +1610,7 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = FrontPortTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', + 'device_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1639,7 +1639,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm): help_text='Select one rear port assignment for each front port being created.', ) field_order = ( - 'manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'rear_port_set', 'description', + 'manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'color', 'rear_port_set', 'description', ) def __init__(self, *args, **kwargs): @@ -1703,6 +1703,11 @@ class FrontPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) description = forms.CharField( required=False ) @@ -1716,7 +1721,7 @@ class RearPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = RearPortTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'positions', 'description', + 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1729,13 +1734,20 @@ class RearPortTemplateCreateForm(ComponentTemplateCreateForm): choices=PortTypeChoices, widget=StaticSelect2(), ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) positions = forms.IntegerField( min_value=REARPORT_POSITIONS_MIN, max_value=REARPORT_POSITIONS_MAX, initial=1, help_text='The number of front ports which may be mapped to each rear port' ) - field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'positions', 'description') + field_order = ( + 'manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'color', 'positions', 'description', + ) class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): @@ -1752,6 +1764,11 @@ class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) description = forms.CharField( required=False ) @@ -3427,7 +3444,7 @@ class InterfaceCSVForm(CustomFieldModelCSVForm): class FrontPortFilterForm(DeviceComponentFilterForm): field_groups = [ - ['name', 'label', 'type'], + ['name', 'label', 'type', 'color'], ['region_id', 'site_group_id', 'site_id'], ['tag'] ] @@ -3437,6 +3454,11 @@ class FrontPortFilterForm(DeviceComponentFilterForm): required=False, widget=StaticSelect2Multiple() ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) tag = TagFilterField(model) @@ -3449,8 +3471,8 @@ class FrontPortForm(BootstrapMixin, CustomFieldModelForm): class Meta: model = FrontPort fields = [ - 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'mark_connected', 'description', - 'tags', + 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected', + 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), @@ -3475,13 +3497,19 @@ class FrontPortCreateForm(ComponentCreateForm): choices=PortTypeChoices, widget=StaticSelect2(), ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) rear_port_set = forms.MultipleChoiceField( choices=[], label='Rear ports', help_text='Select one rear port assignment for each front port being created.', ) field_order = ( - 'device', 'name_pattern', 'label_pattern', 'type', 'rear_port_set', 'mark_connected', 'description', 'tags', + 'device', 'name_pattern', 'label_pattern', 'type', 'color', 'rear_port_set', 'mark_connected', 'description', + 'tags', ) def __init__(self, *args, **kwargs): @@ -3540,7 +3568,7 @@ class FrontPortCreateForm(ComponentCreateForm): class FrontPortBulkEditForm( - form_from_model(FrontPort, ['label', 'type', 'mark_connected', 'description']), + form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']), BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm @@ -3572,7 +3600,8 @@ class FrontPortCSVForm(CustomFieldModelCSVForm): class Meta: model = FrontPort fields = ( - 'device', 'name', 'label', 'type', 'mark_connected', 'rear_port', 'rear_port_position', 'description', + 'device', 'name', 'label', 'type', 'color', 'mark_connected', 'rear_port', 'rear_port_position', + 'description', ) help_texts = { 'rear_port_position': 'Mapped position on corresponding rear port', @@ -3608,7 +3637,7 @@ class FrontPortCSVForm(CustomFieldModelCSVForm): class RearPortFilterForm(DeviceComponentFilterForm): model = RearPort field_groups = [ - ['name', 'label', 'type'], + ['name', 'label', 'type', 'color'], ['region_id', 'site_group_id', 'site_id'], ['tag'] ] @@ -3617,6 +3646,11 @@ class RearPortFilterForm(DeviceComponentFilterForm): required=False, widget=StaticSelect2Multiple() ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) tag = TagFilterField(model) @@ -3629,7 +3663,7 @@ class RearPortForm(BootstrapMixin, CustomFieldModelForm): class Meta: model = RearPort fields = [ - 'device', 'name', 'label', 'type', 'positions', 'mark_connected', 'description', 'tags', + 'device', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), @@ -3643,6 +3677,11 @@ class RearPortCreateForm(ComponentCreateForm): choices=PortTypeChoices, widget=StaticSelect2(), ) + color = forms.CharField( + max_length=6, # RGB color code + required=False, + widget=ColorSelect() + ) positions = forms.IntegerField( min_value=REARPORT_POSITIONS_MIN, max_value=REARPORT_POSITIONS_MAX, @@ -3650,12 +3689,13 @@ class RearPortCreateForm(ComponentCreateForm): help_text='The number of front ports which may be mapped to each rear port' ) field_order = ( - 'device', 'name_pattern', 'label_pattern', 'type', 'positions', 'mark_connected', 'description', 'tags', + 'device', 'name_pattern', 'label_pattern', 'type', 'color', 'positions', 'mark_connected', 'description', + 'tags', ) class RearPortBulkCreateForm( - form_from_model(RearPort, ['type', 'positions', 'mark_connected']), + form_from_model(RearPort, ['type', 'color', 'positions', 'mark_connected']), DeviceBulkAddComponentForm ): model = RearPort @@ -3663,7 +3703,7 @@ class RearPortBulkCreateForm( class RearPortBulkEditForm( - form_from_model(RearPort, ['label', 'type', 'mark_connected', 'description']), + form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']), BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm @@ -3689,7 +3729,7 @@ class RearPortCSVForm(CustomFieldModelCSVForm): class Meta: model = RearPort - fields = ('device', 'name', 'label', 'type', 'mark_connected', 'positions', 'description') + fields = ('device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description') help_texts = { 'positions': 'Number of front ports which may be mapped' } diff --git a/netbox/dcim/migrations/0133_port_colors.py b/netbox/dcim/migrations/0133_port_colors.py new file mode 100644 index 000000000..8cae7ac8e --- /dev/null +++ b/netbox/dcim/migrations/0133_port_colors.py @@ -0,0 +1,32 @@ +from django.db import migrations +import utilities.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0132_cable_length'), + ] + + operations = [ + migrations.AddField( + model_name='frontport', + name='color', + field=utilities.fields.ColorField(blank=True, max_length=6), + ), + migrations.AddField( + model_name='frontporttemplate', + name='color', + field=utilities.fields.ColorField(blank=True, max_length=6), + ), + migrations.AddField( + model_name='rearport', + name='color', + field=utilities.fields.ColorField(blank=True, max_length=6), + ), + migrations.AddField( + model_name='rearporttemplate', + 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 d99d6b7c3..6cfe52fc3 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -6,7 +6,7 @@ from dcim.choices import * from dcim.constants import * from extras.utils import extras_features from netbox.models import ChangeLoggedModel -from utilities.fields import NaturalOrderingField +from utilities.fields import ColorField, NaturalOrderingField from utilities.querysets import RestrictedQuerySet from utilities.ordering import naturalize_interface from .device_components import ( @@ -267,6 +267,9 @@ class FrontPortTemplate(ComponentTemplateModel): max_length=50, choices=PortTypeChoices ) + color = ColorField( + blank=True + ) rear_port = models.ForeignKey( to='dcim.RearPortTemplate', on_delete=models.CASCADE, @@ -314,6 +317,7 @@ class FrontPortTemplate(ComponentTemplateModel): name=self.name, label=self.label, type=self.type, + color=self.color, rear_port=rear_port, rear_port_position=self.rear_port_position ) @@ -328,6 +332,9 @@ class RearPortTemplate(ComponentTemplateModel): max_length=50, choices=PortTypeChoices ) + color = ColorField( + blank=True + ) positions = models.PositiveSmallIntegerField( default=1, validators=[ @@ -346,6 +353,7 @@ class RearPortTemplate(ComponentTemplateModel): name=self.name, label=self.label, type=self.type, + color=self.color, positions=self.positions ) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 1729fdaa4..84a78c525 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -12,7 +12,7 @@ from dcim.constants import * from dcim.fields import MACAddressField from extras.utils import extras_features from netbox.models import PrimaryModel -from utilities.fields import NaturalOrderingField +from utilities.fields import ColorField, NaturalOrderingField from utilities.mptt import TreeManager from utilities.ordering import naturalize_interface from utilities.querysets import RestrictedQuerySet @@ -614,6 +614,9 @@ class FrontPort(ComponentModel, CableTermination): max_length=50, choices=PortTypeChoices ) + color = ColorField( + blank=True + ) rear_port = models.ForeignKey( to='dcim.RearPort', on_delete=models.CASCADE, @@ -663,6 +666,9 @@ class RearPort(ComponentModel, CableTermination): max_length=50, choices=PortTypeChoices ) + color = ColorField( + blank=True + ) positions = models.PositiveSmallIntegerField( default=1, validators=[ diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 24a5927f1..013bfa1ce 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -549,6 +549,7 @@ class FrontPortTable(DeviceComponentTable, CableTerminationTable): 'args': [Accessor('device_id')], } ) + color = ColorColumn() rear_port_position = tables.Column( verbose_name='Position' ) @@ -562,10 +563,12 @@ class FrontPortTable(DeviceComponentTable, CableTerminationTable): class Meta(DeviceComponentTable.Meta): model = FrontPort fields = ( - 'pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'mark_connected', - 'cable', 'cable_color', 'cable_peer', 'tags', + 'pk', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', + 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'tags', + ) + default_columns = ( + 'pk', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', ) - default_columns = ('pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description') class DeviceFrontPortTable(FrontPortTable): @@ -603,6 +606,7 @@ class RearPortTable(DeviceComponentTable, CableTerminationTable): 'args': [Accessor('device_id')], } ) + color = ColorColumn() tags = TagColumn( url_name='dcim:rearport_list' ) @@ -610,10 +614,10 @@ class RearPortTable(DeviceComponentTable, CableTerminationTable): class Meta(DeviceComponentTable.Meta): model = RearPort fields = ( - 'pk', 'device', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', 'cable', + 'pk', 'device', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'tags', ) - default_columns = ('pk', 'device', 'name', 'label', 'type', 'description') + default_columns = ('pk', 'device', 'name', 'label', 'type', 'color', 'description') class DeviceRearPortTable(RearPortTable): diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index 0a445171d..6720baf66 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -4,7 +4,9 @@ from dcim.models import ( ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, FrontPortTemplate, InterfaceTemplate, Manufacturer, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate, ) -from utilities.tables import BaseTable, BooleanColumn, ButtonsColumn, LinkedCountColumn, TagColumn, ToggleColumn +from utilities.tables import ( + BaseTable, BooleanColumn, ButtonsColumn, ColorColumn, LinkedCountColumn, TagColumn, ToggleColumn, +) __all__ = ( 'ConsolePortTemplateTable', @@ -164,6 +166,7 @@ class FrontPortTemplateTable(ComponentTemplateTable): rear_port_position = tables.Column( verbose_name='Position' ) + color = ColorColumn() actions = ButtonsColumn( model=FrontPortTemplate, buttons=('edit', 'delete'), @@ -172,11 +175,12 @@ class FrontPortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = FrontPortTemplate - fields = ('pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'actions') + fields = ('pk', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', 'actions') empty_text = "None" class RearPortTemplateTable(ComponentTemplateTable): + color = ColorColumn() actions = ButtonsColumn( model=RearPortTemplate, buttons=('edit', 'delete'), @@ -185,7 +189,7 @@ class RearPortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = RearPortTemplate - fields = ('pk', 'name', 'label', 'type', 'positions', 'description', 'actions') + fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'description', 'actions') empty_text = "None" diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 154ec0847..d17bdd3f7 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -6,6 +6,7 @@ from dcim.filtersets import * from dcim.models import * from ipam.models import IPAddress from tenancy.models import Tenant, TenantGroup +from utilities.choices import ColorChoices from utilities.testing import ChangeLoggedFilterSetTests from virtualization.models import Cluster, ClusterType @@ -959,9 +960,9 @@ class FrontPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): RearPortTemplate.objects.bulk_create(rear_ports) FrontPortTemplate.objects.bulk_create(( - FrontPortTemplate(device_type=device_types[0], name='Front Port 1', rear_port=rear_ports[0], type=PortTypeChoices.TYPE_8P8C), - FrontPortTemplate(device_type=device_types[1], name='Front Port 2', rear_port=rear_ports[1], type=PortTypeChoices.TYPE_110_PUNCH), - FrontPortTemplate(device_type=device_types[2], name='Front Port 3', rear_port=rear_ports[2], type=PortTypeChoices.TYPE_BNC), + FrontPortTemplate(device_type=device_types[0], name='Front Port 1', rear_port=rear_ports[0], type=PortTypeChoices.TYPE_8P8C, color=ColorChoices.COLOR_RED), + FrontPortTemplate(device_type=device_types[1], name='Front Port 2', rear_port=rear_ports[1], type=PortTypeChoices.TYPE_110_PUNCH, color=ColorChoices.COLOR_GREEN), + FrontPortTemplate(device_type=device_types[2], name='Front Port 3', rear_port=rear_ports[2], type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE), )) def test_name(self): @@ -977,6 +978,10 @@ class FrontPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]} 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 RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = RearPortTemplate.objects.all() @@ -995,9 +1000,9 @@ class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceType.objects.bulk_create(device_types) RearPortTemplate.objects.bulk_create(( - RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C, positions=1), - RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PortTypeChoices.TYPE_110_PUNCH, positions=2), - RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, positions=3), + RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C, color=ColorChoices.COLOR_RED, positions=1), + RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PortTypeChoices.TYPE_110_PUNCH, color=ColorChoices.COLOR_GREEN, positions=2), + RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE, positions=3), )) def test_name(self): @@ -1013,6 +1018,10 @@ class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]} 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) + def test_positions(self): params = {'positions': [1, 2]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -2153,9 +2162,9 @@ class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests): RearPort.objects.bulk_create(rear_ports) front_ports = ( - FrontPort(device=devices[0], name='Front Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0], rear_port_position=1, description='First'), - FrontPort(device=devices[1], name='Front Port 2', label='B', type=PortTypeChoices.TYPE_110_PUNCH, rear_port=rear_ports[1], rear_port_position=2, description='Second'), - FrontPort(device=devices[2], name='Front Port 3', label='C', type=PortTypeChoices.TYPE_BNC, rear_port=rear_ports[2], rear_port_position=3, description='Third'), + FrontPort(device=devices[0], name='Front Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, color=ColorChoices.COLOR_RED, rear_port=rear_ports[0], rear_port_position=1, description='First'), + FrontPort(device=devices[1], name='Front Port 2', label='B', type=PortTypeChoices.TYPE_110_PUNCH, color=ColorChoices.COLOR_GREEN, rear_port=rear_ports[1], rear_port_position=2, description='Second'), + FrontPort(device=devices[2], name='Front Port 3', label='C', type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE, rear_port=rear_ports[2], rear_port_position=3, description='Third'), FrontPort(device=devices[3], name='Front Port 4', label='D', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[3], rear_port_position=1), FrontPort(device=devices[3], name='Front Port 5', label='E', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[4], rear_port_position=1), FrontPort(device=devices[3], name='Front Port 6', label='F', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[5], rear_port_position=1), @@ -2179,6 +2188,10 @@ class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]} 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) + def test_description(self): params = {'description': ['First', 'Second']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -2260,9 +2273,9 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests): Device.objects.bulk_create(devices) rear_ports = ( - RearPort(device=devices[0], name='Rear Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, positions=1, description='First'), - RearPort(device=devices[1], name='Rear Port 2', label='B', type=PortTypeChoices.TYPE_110_PUNCH, positions=2, description='Second'), - RearPort(device=devices[2], name='Rear Port 3', label='C', type=PortTypeChoices.TYPE_BNC, positions=3, description='Third'), + RearPort(device=devices[0], name='Rear Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, color=ColorChoices.COLOR_RED, positions=1, description='First'), + RearPort(device=devices[1], name='Rear Port 2', label='B', type=PortTypeChoices.TYPE_110_PUNCH, color=ColorChoices.COLOR_GREEN, positions=2, description='Second'), + RearPort(device=devices[2], name='Rear Port 3', label='C', type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE, positions=3, description='Third'), RearPort(device=devices[3], name='Rear Port 4', label='D', type=PortTypeChoices.TYPE_FC, positions=4), RearPort(device=devices[3], name='Rear Port 5', label='E', type=PortTypeChoices.TYPE_FC, positions=5), RearPort(device=devices[3], name='Rear Port 6', label='F', type=PortTypeChoices.TYPE_FC, positions=6), @@ -2286,6 +2299,10 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]} 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) + def test_positions(self): params = {'positions': [1, 2]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) diff --git a/netbox/templates/dcim/frontport.html b/netbox/templates/dcim/frontport.html index ac9137231..c5b2efe8d 100644 --- a/netbox/templates/dcim/frontport.html +++ b/netbox/templates/dcim/frontport.html @@ -35,6 +35,12 @@ Type {{ object.get_type_display }} + + Color + +   + + Rear Port diff --git a/netbox/templates/dcim/rearport.html b/netbox/templates/dcim/rearport.html index 31397bf6d..5fb330844 100644 --- a/netbox/templates/dcim/rearport.html +++ b/netbox/templates/dcim/rearport.html @@ -34,6 +34,12 @@ Type {{ object.get_type_display }} + + Color + +   + + Positions {{ object.positions }}