Closes #3682: Add color field to front and rear ports

This commit is contained in:
jeremystretch 2021-06-09 16:51:51 -04:00
parent 8a7473765e
commit b3ed545d6a
12 changed files with 183 additions and 50 deletions

View File

@ -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 * [#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 * [#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 * [#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 * [#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 * [#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) * Removed the `display_name` attribute (use `display` instead)
* dcim.DeviceType * dcim.DeviceType
* Removed the `display_name` attribute (use `display` instead) * Removed the `display_name` attribute (use `display` instead)
* dcim.FrontPort
* Added `color` field
* dcim.FrontPortTemplate
* Added `color` field
* dcim.Rack * dcim.Rack
* Removed the `display_name` attribute (use `display` instead) * Removed the `display_name` attribute (use `display` instead)
* dcim.RearPort
* Added `color` field
* dcim.RearPortTemplate
* Added `color` field
* dcim.Site * dcim.Site
* `latitude` and `longitude` are now decimal fields rather than strings * `latitude` and `longitude` are now decimal fields rather than strings
* extras.ContentType * extras.ContentType

View File

@ -384,8 +384,8 @@ class RearPortTemplateSerializer(ValidatedModelSerializer):
class Meta: class Meta:
model = RearPortTemplate model = RearPortTemplate
fields = [ fields = [
'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'positions', 'description', 'created', 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description',
'last_updated', 'created', 'last_updated',
] ]
@ -398,7 +398,7 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer):
class Meta: class Meta:
model = FrontPortTemplate model = FrontPortTemplate
fields = [ 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', 'description', 'created', 'last_updated',
] ]
@ -665,8 +665,9 @@ class RearPortSerializer(PrimaryModelSerializer, CableTerminationSerializer):
class Meta: class Meta:
model = RearPort model = RearPort
fields = [ fields = [
'id', 'url', 'display', 'device', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'positions', 'description',
'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', '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: class Meta:
model = FrontPort model = FrontPort
fields = [ fields = [
'id', 'url', 'display', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', 'description', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields',
'last_updated', '_occupied', 'created', 'last_updated', '_occupied',
] ]

View File

@ -538,7 +538,7 @@ class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponent
class Meta: class Meta:
model = FrontPortTemplate model = FrontPortTemplate
fields = ['id', 'name', 'type'] fields = ['id', 'name', 'type', 'color']
class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
@ -549,7 +549,7 @@ class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentF
class Meta: class Meta:
model = RearPortTemplate model = RearPortTemplate
fields = ['id', 'name', 'type', 'positions'] fields = ['id', 'name', 'type', 'color', 'positions']
class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
@ -1027,7 +1027,7 @@ class FrontPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableT
class Meta: class Meta:
model = FrontPort model = FrontPort
fields = ['id', 'name', 'label', 'type', 'description'] fields = ['id', 'name', 'label', 'type', 'color', 'description']
class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet): class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet):
@ -1038,7 +1038,7 @@ class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTe
class Meta: class Meta:
model = RearPort model = RearPort
fields = ['id', 'name', 'label', 'type', 'positions', 'description'] fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description']
class DeviceBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet): class DeviceBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):

View File

@ -1610,7 +1610,7 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm):
class Meta: class Meta:
model = FrontPortTemplate model = FrontPortTemplate
fields = [ 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 = { widgets = {
'device_type': forms.HiddenInput(), 'device_type': forms.HiddenInput(),
@ -1639,7 +1639,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm):
help_text='Select one rear port assignment for each front port being created.', help_text='Select one rear port assignment for each front port being created.',
) )
field_order = ( 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): def __init__(self, *args, **kwargs):
@ -1703,6 +1703,11 @@ class FrontPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
required=False, required=False,
widget=StaticSelect2() widget=StaticSelect2()
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
description = forms.CharField( description = forms.CharField(
required=False required=False
) )
@ -1716,7 +1721,7 @@ class RearPortTemplateForm(BootstrapMixin, forms.ModelForm):
class Meta: class Meta:
model = RearPortTemplate model = RearPortTemplate
fields = [ fields = [
'device_type', 'name', 'label', 'type', 'positions', 'description', 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description',
] ]
widgets = { widgets = {
'device_type': forms.HiddenInput(), 'device_type': forms.HiddenInput(),
@ -1729,13 +1734,20 @@ class RearPortTemplateCreateForm(ComponentTemplateCreateForm):
choices=PortTypeChoices, choices=PortTypeChoices,
widget=StaticSelect2(), widget=StaticSelect2(),
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
positions = forms.IntegerField( positions = forms.IntegerField(
min_value=REARPORT_POSITIONS_MIN, min_value=REARPORT_POSITIONS_MIN,
max_value=REARPORT_POSITIONS_MAX, max_value=REARPORT_POSITIONS_MAX,
initial=1, initial=1,
help_text='The number of front ports which may be mapped to each rear port' 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): class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
@ -1752,6 +1764,11 @@ class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
required=False, required=False,
widget=StaticSelect2() widget=StaticSelect2()
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
description = forms.CharField( description = forms.CharField(
required=False required=False
) )
@ -3427,7 +3444,7 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
class FrontPortFilterForm(DeviceComponentFilterForm): class FrontPortFilterForm(DeviceComponentFilterForm):
field_groups = [ field_groups = [
['name', 'label', 'type'], ['name', 'label', 'type', 'color'],
['region_id', 'site_group_id', 'site_id'], ['region_id', 'site_group_id', 'site_id'],
['tag'] ['tag']
] ]
@ -3437,6 +3454,11 @@ class FrontPortFilterForm(DeviceComponentFilterForm):
required=False, required=False,
widget=StaticSelect2Multiple() widget=StaticSelect2Multiple()
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
tag = TagFilterField(model) tag = TagFilterField(model)
@ -3449,8 +3471,8 @@ class FrontPortForm(BootstrapMixin, CustomFieldModelForm):
class Meta: class Meta:
model = FrontPort model = FrontPort
fields = [ fields = [
'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'mark_connected', 'description', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected',
'tags', 'description', 'tags',
] ]
widgets = { widgets = {
'device': forms.HiddenInput(), 'device': forms.HiddenInput(),
@ -3475,13 +3497,19 @@ class FrontPortCreateForm(ComponentCreateForm):
choices=PortTypeChoices, choices=PortTypeChoices,
widget=StaticSelect2(), widget=StaticSelect2(),
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
rear_port_set = forms.MultipleChoiceField( rear_port_set = forms.MultipleChoiceField(
choices=[], choices=[],
label='Rear ports', label='Rear ports',
help_text='Select one rear port assignment for each front port being created.', help_text='Select one rear port assignment for each front port being created.',
) )
field_order = ( 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): def __init__(self, *args, **kwargs):
@ -3540,7 +3568,7 @@ class FrontPortCreateForm(ComponentCreateForm):
class FrontPortBulkEditForm( class FrontPortBulkEditForm(
form_from_model(FrontPort, ['label', 'type', 'mark_connected', 'description']), form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']),
BootstrapMixin, BootstrapMixin,
AddRemoveTagsForm, AddRemoveTagsForm,
CustomFieldBulkEditForm CustomFieldBulkEditForm
@ -3572,7 +3600,8 @@ class FrontPortCSVForm(CustomFieldModelCSVForm):
class Meta: class Meta:
model = FrontPort model = FrontPort
fields = ( 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 = { help_texts = {
'rear_port_position': 'Mapped position on corresponding rear port', 'rear_port_position': 'Mapped position on corresponding rear port',
@ -3608,7 +3637,7 @@ class FrontPortCSVForm(CustomFieldModelCSVForm):
class RearPortFilterForm(DeviceComponentFilterForm): class RearPortFilterForm(DeviceComponentFilterForm):
model = RearPort model = RearPort
field_groups = [ field_groups = [
['name', 'label', 'type'], ['name', 'label', 'type', 'color'],
['region_id', 'site_group_id', 'site_id'], ['region_id', 'site_group_id', 'site_id'],
['tag'] ['tag']
] ]
@ -3617,6 +3646,11 @@ class RearPortFilterForm(DeviceComponentFilterForm):
required=False, required=False,
widget=StaticSelect2Multiple() widget=StaticSelect2Multiple()
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
tag = TagFilterField(model) tag = TagFilterField(model)
@ -3629,7 +3663,7 @@ class RearPortForm(BootstrapMixin, CustomFieldModelForm):
class Meta: class Meta:
model = RearPort model = RearPort
fields = [ fields = [
'device', 'name', 'label', 'type', 'positions', 'mark_connected', 'description', 'tags', 'device', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags',
] ]
widgets = { widgets = {
'device': forms.HiddenInput(), 'device': forms.HiddenInput(),
@ -3643,6 +3677,11 @@ class RearPortCreateForm(ComponentCreateForm):
choices=PortTypeChoices, choices=PortTypeChoices,
widget=StaticSelect2(), widget=StaticSelect2(),
) )
color = forms.CharField(
max_length=6, # RGB color code
required=False,
widget=ColorSelect()
)
positions = forms.IntegerField( positions = forms.IntegerField(
min_value=REARPORT_POSITIONS_MIN, min_value=REARPORT_POSITIONS_MIN,
max_value=REARPORT_POSITIONS_MAX, 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' help_text='The number of front ports which may be mapped to each rear port'
) )
field_order = ( 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( class RearPortBulkCreateForm(
form_from_model(RearPort, ['type', 'positions', 'mark_connected']), form_from_model(RearPort, ['type', 'color', 'positions', 'mark_connected']),
DeviceBulkAddComponentForm DeviceBulkAddComponentForm
): ):
model = RearPort model = RearPort
@ -3663,7 +3703,7 @@ class RearPortBulkCreateForm(
class RearPortBulkEditForm( class RearPortBulkEditForm(
form_from_model(RearPort, ['label', 'type', 'mark_connected', 'description']), form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']),
BootstrapMixin, BootstrapMixin,
AddRemoveTagsForm, AddRemoveTagsForm,
CustomFieldBulkEditForm CustomFieldBulkEditForm
@ -3689,7 +3729,7 @@ class RearPortCSVForm(CustomFieldModelCSVForm):
class Meta: class Meta:
model = RearPort model = RearPort
fields = ('device', 'name', 'label', 'type', 'mark_connected', 'positions', 'description') fields = ('device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description')
help_texts = { help_texts = {
'positions': 'Number of front ports which may be mapped' 'positions': 'Number of front ports which may be mapped'
} }

View File

@ -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),
),
]

View File

@ -6,7 +6,7 @@ from dcim.choices import *
from dcim.constants import * from dcim.constants import *
from extras.utils import extras_features from extras.utils import extras_features
from netbox.models import ChangeLoggedModel from netbox.models import ChangeLoggedModel
from utilities.fields import NaturalOrderingField from utilities.fields import ColorField, NaturalOrderingField
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
from utilities.ordering import naturalize_interface from utilities.ordering import naturalize_interface
from .device_components import ( from .device_components import (
@ -267,6 +267,9 @@ class FrontPortTemplate(ComponentTemplateModel):
max_length=50, max_length=50,
choices=PortTypeChoices choices=PortTypeChoices
) )
color = ColorField(
blank=True
)
rear_port = models.ForeignKey( rear_port = models.ForeignKey(
to='dcim.RearPortTemplate', to='dcim.RearPortTemplate',
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -314,6 +317,7 @@ class FrontPortTemplate(ComponentTemplateModel):
name=self.name, name=self.name,
label=self.label, label=self.label,
type=self.type, type=self.type,
color=self.color,
rear_port=rear_port, rear_port=rear_port,
rear_port_position=self.rear_port_position rear_port_position=self.rear_port_position
) )
@ -328,6 +332,9 @@ class RearPortTemplate(ComponentTemplateModel):
max_length=50, max_length=50,
choices=PortTypeChoices choices=PortTypeChoices
) )
color = ColorField(
blank=True
)
positions = models.PositiveSmallIntegerField( positions = models.PositiveSmallIntegerField(
default=1, default=1,
validators=[ validators=[
@ -346,6 +353,7 @@ class RearPortTemplate(ComponentTemplateModel):
name=self.name, name=self.name,
label=self.label, label=self.label,
type=self.type, type=self.type,
color=self.color,
positions=self.positions positions=self.positions
) )

View File

@ -12,7 +12,7 @@ from dcim.constants import *
from dcim.fields import MACAddressField from dcim.fields import MACAddressField
from extras.utils import extras_features from extras.utils import extras_features
from netbox.models import PrimaryModel from netbox.models import PrimaryModel
from utilities.fields import NaturalOrderingField from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager from utilities.mptt import TreeManager
from utilities.ordering import naturalize_interface from utilities.ordering import naturalize_interface
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
@ -614,6 +614,9 @@ class FrontPort(ComponentModel, CableTermination):
max_length=50, max_length=50,
choices=PortTypeChoices choices=PortTypeChoices
) )
color = ColorField(
blank=True
)
rear_port = models.ForeignKey( rear_port = models.ForeignKey(
to='dcim.RearPort', to='dcim.RearPort',
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -663,6 +666,9 @@ class RearPort(ComponentModel, CableTermination):
max_length=50, max_length=50,
choices=PortTypeChoices choices=PortTypeChoices
) )
color = ColorField(
blank=True
)
positions = models.PositiveSmallIntegerField( positions = models.PositiveSmallIntegerField(
default=1, default=1,
validators=[ validators=[

View File

@ -549,6 +549,7 @@ class FrontPortTable(DeviceComponentTable, CableTerminationTable):
'args': [Accessor('device_id')], 'args': [Accessor('device_id')],
} }
) )
color = ColorColumn()
rear_port_position = tables.Column( rear_port_position = tables.Column(
verbose_name='Position' verbose_name='Position'
) )
@ -562,10 +563,12 @@ class FrontPortTable(DeviceComponentTable, CableTerminationTable):
class Meta(DeviceComponentTable.Meta): class Meta(DeviceComponentTable.Meta):
model = FrontPort model = FrontPort
fields = ( fields = (
'pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'mark_connected', 'pk', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description',
'cable', 'cable_color', 'cable_peer', 'tags', '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): class DeviceFrontPortTable(FrontPortTable):
@ -603,6 +606,7 @@ class RearPortTable(DeviceComponentTable, CableTerminationTable):
'args': [Accessor('device_id')], 'args': [Accessor('device_id')],
} }
) )
color = ColorColumn()
tags = TagColumn( tags = TagColumn(
url_name='dcim:rearport_list' url_name='dcim:rearport_list'
) )
@ -610,10 +614,10 @@ class RearPortTable(DeviceComponentTable, CableTerminationTable):
class Meta(DeviceComponentTable.Meta): class Meta(DeviceComponentTable.Meta):
model = RearPort model = RearPort
fields = ( 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', '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): class DeviceRearPortTable(RearPortTable):

View File

@ -4,7 +4,9 @@ from dcim.models import (
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, FrontPortTemplate, InterfaceTemplate, ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, FrontPortTemplate, InterfaceTemplate,
Manufacturer, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate, 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__ = ( __all__ = (
'ConsolePortTemplateTable', 'ConsolePortTemplateTable',
@ -164,6 +166,7 @@ class FrontPortTemplateTable(ComponentTemplateTable):
rear_port_position = tables.Column( rear_port_position = tables.Column(
verbose_name='Position' verbose_name='Position'
) )
color = ColorColumn()
actions = ButtonsColumn( actions = ButtonsColumn(
model=FrontPortTemplate, model=FrontPortTemplate,
buttons=('edit', 'delete'), buttons=('edit', 'delete'),
@ -172,11 +175,12 @@ class FrontPortTemplateTable(ComponentTemplateTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = FrontPortTemplate 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" empty_text = "None"
class RearPortTemplateTable(ComponentTemplateTable): class RearPortTemplateTable(ComponentTemplateTable):
color = ColorColumn()
actions = ButtonsColumn( actions = ButtonsColumn(
model=RearPortTemplate, model=RearPortTemplate,
buttons=('edit', 'delete'), buttons=('edit', 'delete'),
@ -185,7 +189,7 @@ class RearPortTemplateTable(ComponentTemplateTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = RearPortTemplate model = RearPortTemplate
fields = ('pk', 'name', 'label', 'type', 'positions', 'description', 'actions') fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'description', 'actions')
empty_text = "None" empty_text = "None"

View File

@ -6,6 +6,7 @@ from dcim.filtersets import *
from dcim.models import * from dcim.models import *
from ipam.models import IPAddress from ipam.models import IPAddress
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from utilities.choices import ColorChoices
from utilities.testing import ChangeLoggedFilterSetTests from utilities.testing import ChangeLoggedFilterSetTests
from virtualization.models import Cluster, ClusterType from virtualization.models import Cluster, ClusterType
@ -959,9 +960,9 @@ class FrontPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
RearPortTemplate.objects.bulk_create(rear_ports) RearPortTemplate.objects.bulk_create(rear_ports)
FrontPortTemplate.objects.bulk_create(( 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[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), 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), 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): def test_name(self):
@ -977,6 +978,10 @@ class FrontPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]} params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) 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): class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RearPortTemplate.objects.all() queryset = RearPortTemplate.objects.all()
@ -995,9 +1000,9 @@ class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
DeviceType.objects.bulk_create(device_types) DeviceType.objects.bulk_create(device_types)
RearPortTemplate.objects.bulk_create(( 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[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, positions=2), 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, positions=3), RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE, positions=3),
)) ))
def test_name(self): def test_name(self):
@ -1013,6 +1018,10 @@ class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]} params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) 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): def test_positions(self):
params = {'positions': [1, 2]} params = {'positions': [1, 2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 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) RearPort.objects.bulk_create(rear_ports)
front_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[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, rear_port=rear_ports[1], rear_port_position=2, description='Second'), 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, rear_port=rear_ports[2], rear_port_position=3, description='Third'), 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 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 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), 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]} params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) 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): def test_description(self):
params = {'description': ['First', 'Second']} params = {'description': ['First', 'Second']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -2260,9 +2273,9 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
Device.objects.bulk_create(devices) Device.objects.bulk_create(devices)
rear_ports = ( rear_ports = (
RearPort(device=devices[0], name='Rear Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, positions=1, description='First'), 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, positions=2, description='Second'), 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, positions=3, description='Third'), 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 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 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), 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]} params = {'type': [PortTypeChoices.TYPE_8P8C, PortTypeChoices.TYPE_110_PUNCH]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) 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): def test_positions(self):
params = {'positions': [1, 2]} params = {'positions': [1, 2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -35,6 +35,12 @@
<th scope="row">Type</th> <th scope="row">Type</th>
<td>{{ object.get_type_display }}</td> <td>{{ object.get_type_display }}</td>
</tr> </tr>
<tr>
<th scope="row">Color</th>
<td>
<span class="badge color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
</td>
</tr>
<tr> <tr>
<th scope="row">Rear Port</th> <th scope="row">Rear Port</th>
<td> <td>

View File

@ -34,6 +34,12 @@
<th scope="row">Type</th> <th scope="row">Type</th>
<td>{{ object.get_type_display }}</td> <td>{{ object.get_type_display }}</td>
</tr> </tr>
<tr>
<th scope="row">Color</th>
<td>
<span class="badge color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
</td>
</tr>
<tr> <tr>
<th scope="row">Positions</th> <th scope="row">Positions</th>
<td>{{ object.positions }}</td> <td>{{ object.positions }}</td>