From eaa1165611999bb5a44980622f9335c0da3cda93 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 20 Dec 2021 09:51:55 -0500 Subject: [PATCH] Add position field for module bays --- netbox/dcim/api/serializers.py | 9 ++++-- netbox/dcim/forms/bulk_edit.py | 4 +-- netbox/dcim/forms/bulk_import.py | 2 +- netbox/dcim/forms/filtersets.py | 5 +++- netbox/dcim/forms/models.py | 4 +-- netbox/dcim/forms/object_create.py | 24 +++++++++------- netbox/dcim/forms/object_import.py | 2 +- netbox/dcim/migrations/0145_modules.py | 2 ++ .../dcim/models/device_component_templates.py | 28 +++++++++++++------ netbox/dcim/models/device_components.py | 6 ++++ netbox/dcim/tables/devices.py | 2 +- netbox/dcim/tables/devicetypes.py | 2 +- netbox/templates/dcim/modulebay.html | 4 +++ netbox/templates/dcim/moduletype/base.html | 2 +- 14 files changed, 65 insertions(+), 31 deletions(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index c81b26d1f..cf6c89333 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -432,7 +432,10 @@ class ModuleBayTemplateSerializer(ValidatedModelSerializer): class Meta: model = ModuleBayTemplate - fields = ['id', 'url', 'display', 'device_type', 'name', 'label', 'description', 'created', 'last_updated'] + fields = [ + 'id', 'url', 'display', 'device_type', 'name', 'label', 'position', 'description', 'created', + 'last_updated', + ] class DeviceBayTemplateSerializer(ValidatedModelSerializer): @@ -785,8 +788,8 @@ class ModuleBaySerializer(PrimaryModelSerializer): class Meta: model = ModuleBay fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'description', 'tags', 'custom_fields', 'created', - 'last_updated', + 'id', 'url', 'display', 'device', 'name', 'label', 'position', 'description', 'tags', 'custom_fields', + 'created', 'last_updated', ] diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index 378620180..d40ac6fca 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -886,7 +886,7 @@ class ModuleBayTemplateBulkEditForm(BulkEditForm): ) class Meta: - nullable_fields = ('label', 'description') + nullable_fields = ('label', 'position', 'description') class DeviceBayTemplateBulkEditForm(BulkEditForm): @@ -1153,7 +1153,7 @@ class ModuleBayBulkEditForm( ) class Meta: - nullable_fields = ['label', 'description'] + nullable_fields = ['label', 'position', 'description'] class DeviceBayBulkEditForm( diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 8f5ba25b7..23d589abf 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -717,7 +717,7 @@ class ModuleBayCSVForm(CustomFieldModelCSVForm): class Meta: model = ModuleBay - fields = ('device', 'name', 'label', 'description') + fields = ('device', 'name', 'label', 'position', 'description') class DeviceBayCSVForm(CustomFieldModelCSVForm): diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 29c09c7f7..819cb91cc 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -1073,10 +1073,13 @@ class ModuleBayFilterForm(DeviceComponentFilterForm): model = ModuleBay field_groups = [ ['q', 'tag'], - ['name', 'label'], + ['name', 'label', 'position'], ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] tag = TagFilterField(model) + position = forms.CharField( + required=False + ) class DeviceBayFilterForm(DeviceComponentFilterForm): diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 672c54c68..2d32093c4 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -1059,7 +1059,7 @@ class ModuleBayTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = ModuleBayTemplate fields = [ - 'device_type', 'name', 'label', 'description', + 'device_type', 'name', 'label', 'position', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1313,7 +1313,7 @@ class ModuleBayForm(CustomFieldModelForm): class Meta: model = ModuleBay fields = [ - 'device', 'name', 'label', 'description', 'tags', + 'device', 'name', 'label', 'position', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index 1619f5424..681a17a5a 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -163,6 +163,12 @@ class ComponentTemplateCreateForm(ComponentForm): 'manufacturer_id': '$manufacturer' } ) + description = forms.CharField( + required=False + ) + + +class ModularComponentTemplateCreateForm(ComponentTemplateCreateForm): module_type = DynamicModelChoiceField( queryset=ModuleType.objects.all(), required=False, @@ -170,12 +176,9 @@ class ComponentTemplateCreateForm(ComponentForm): 'manufacturer_id': '$manufacturer' } ) - description = forms.CharField( - required=False - ) -class ConsolePortTemplateCreateForm(ComponentTemplateCreateForm): +class ConsolePortTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=add_blank_choice(ConsolePortTypeChoices), widget=StaticSelect() @@ -185,7 +188,7 @@ class ConsolePortTemplateCreateForm(ComponentTemplateCreateForm): ) -class ConsoleServerPortTemplateCreateForm(ComponentTemplateCreateForm): +class ConsoleServerPortTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=add_blank_choice(ConsolePortTypeChoices), widget=StaticSelect() @@ -195,7 +198,7 @@ class ConsoleServerPortTemplateCreateForm(ComponentTemplateCreateForm): ) -class PowerPortTemplateCreateForm(ComponentTemplateCreateForm): +class PowerPortTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=add_blank_choice(PowerPortTypeChoices), required=False @@ -216,7 +219,7 @@ class PowerPortTemplateCreateForm(ComponentTemplateCreateForm): ) -class PowerOutletTemplateCreateForm(ComponentTemplateCreateForm): +class PowerOutletTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=add_blank_choice(PowerOutletTypeChoices), required=False @@ -240,7 +243,7 @@ class PowerOutletTemplateCreateForm(ComponentTemplateCreateForm): ) -class InterfaceTemplateCreateForm(ComponentTemplateCreateForm): +class InterfaceTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=InterfaceTypeChoices, widget=StaticSelect() @@ -255,7 +258,7 @@ class InterfaceTemplateCreateForm(ComponentTemplateCreateForm): ) -class FrontPortTemplateCreateForm(ComponentTemplateCreateForm): +class FrontPortTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=PortTypeChoices, widget=StaticSelect() @@ -320,7 +323,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm): } -class RearPortTemplateCreateForm(ComponentTemplateCreateForm): +class RearPortTemplateCreateForm(ModularComponentTemplateCreateForm): type = forms.ChoiceField( choices=PortTypeChoices, widget=StaticSelect(), @@ -341,6 +344,7 @@ class RearPortTemplateCreateForm(ComponentTemplateCreateForm): class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm): + # TODO: Support patterned position assignment field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description') diff --git a/netbox/dcim/forms/object_import.py b/netbox/dcim/forms/object_import.py index cc0c7dc41..36c6ae8bc 100644 --- a/netbox/dcim/forms/object_import.py +++ b/netbox/dcim/forms/object_import.py @@ -155,7 +155,7 @@ class ModuleBayTemplateImportForm(ComponentTemplateImportForm): class Meta: model = ModuleBayTemplate fields = [ - 'device_type', 'name', 'label', 'description', + 'device_type', 'name', 'label', 'position', 'description', ] diff --git a/netbox/dcim/migrations/0145_modules.py b/netbox/dcim/migrations/0145_modules.py index c9a332846..1f99c7c04 100644 --- a/netbox/dcim/migrations/0145_modules.py +++ b/netbox/dcim/migrations/0145_modules.py @@ -105,6 +105,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=64)), ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), ('label', models.CharField(blank=True, max_length=64)), + ('position', models.CharField(blank=True, max_length=30)), ('description', models.CharField(blank=True, max_length=200)), ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modulebays', to='dcim.device')), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), @@ -241,6 +242,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=64)), ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), ('label', models.CharField(blank=True, max_length=64)), + ('position', models.CharField(blank=True, max_length=30)), ('description', models.CharField(blank=True, max_length=200)), ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modulebaytemplates', to='dcim.devicetype')), ], diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index a22118de0..71fed25c5 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -123,6 +123,11 @@ class ModularComponentTemplateModel(ComponentTemplateModel): "A component template must be associated with either a device type or a module type." ) + def resolve_name(self, module): + if module: + return self.name.replace('{module}', module.module_bay.position) + return self.name + @extras_features('webhooks') class ConsolePortTemplate(ModularComponentTemplateModel): @@ -144,7 +149,7 @@ class ConsolePortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return ConsolePort( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, **kwargs @@ -171,7 +176,7 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return ConsoleServerPort( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, **kwargs @@ -210,7 +215,7 @@ class PowerPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return PowerPort( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, maximum_draw=self.maximum_draw, @@ -279,7 +284,7 @@ class PowerOutletTemplate(ModularComponentTemplateModel): else: power_port = None return PowerOutlet( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, power_port=power_port, @@ -318,7 +323,7 @@ class InterfaceTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return Interface( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, mgmt_only=self.mgmt_only, @@ -387,7 +392,7 @@ class FrontPortTemplate(ModularComponentTemplateModel): else: rear_port = None return FrontPort( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, color=self.color, @@ -426,7 +431,7 @@ class RearPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return RearPort( - name=self.name, + name=self.resolve_name(kwargs.get('module')), label=self.label, type=self.type, color=self.color, @@ -440,6 +445,12 @@ class ModuleBayTemplate(ComponentTemplateModel): """ A template for a ModuleBay to be created for a new parent Device. """ + position = models.CharField( + max_length=30, + blank=True, + help_text='Identifier to reference when renaming installed components' + ) + class Meta: ordering = ('device_type', '_name') unique_together = ('device_type', 'name') @@ -448,7 +459,8 @@ class ModuleBayTemplate(ComponentTemplateModel): return ModuleBay( device=device, name=self.name, - label=self.label + label=self.label, + position=self.position ) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index fc80b29c9..ccfe538d7 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -880,6 +880,12 @@ class ModuleBay(ComponentModel): """ An empty space within a Device which can house a child device """ + position = models.CharField( + max_length=30, + blank=True, + help_text='Identifier to reference when renaming installed components' + ) + clone_fields = ['device'] class Meta: diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 08f229d33..7805e60c1 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -749,7 +749,7 @@ class ModuleBayTable(DeviceComponentTable): class Meta(DeviceComponentTable.Meta): model = ModuleBay - fields = ('pk', 'id', 'name', 'device', 'label', 'installed_module', 'description', 'tags') + fields = ('pk', 'id', 'name', 'device', 'label', 'position', 'installed_module', 'description', 'tags') default_columns = ('pk', 'name', 'device', 'label', 'installed_module', 'description') diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index 6fc038542..38a33c652 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -217,7 +217,7 @@ class ModuleBayTemplateTable(ComponentTemplateTable): class Meta(ComponentTemplateTable.Meta): model = ModuleBayTemplate - fields = ('pk', 'name', 'label', 'description', 'actions') + fields = ('pk', 'name', 'label', 'position', 'description', 'actions') empty_text = "None" diff --git a/netbox/templates/dcim/modulebay.html b/netbox/templates/dcim/modulebay.html index 0f903483a..9f04ba225 100644 --- a/netbox/templates/dcim/modulebay.html +++ b/netbox/templates/dcim/modulebay.html @@ -23,6 +23,10 @@ Label {{ object.label|placeholder }} + + Position + {{ object.position|placeholder }} + Description {{ object.description|placeholder }} diff --git a/netbox/templates/dcim/moduletype/base.html b/netbox/templates/dcim/moduletype/base.html index 563e23a7b..2d0bca7d8 100644 --- a/netbox/templates/dcim/moduletype/base.html +++ b/netbox/templates/dcim/moduletype/base.html @@ -45,7 +45,7 @@ {% block tab_items %}