Răsfoiți Sursa

Add position field for module bays

jeremystretch 4 ani în urmă
părinte
comite
eaa1165611

+ 6 - 3
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',
         ]
 
 

+ 2 - 2
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(

+ 1 - 1
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):

+ 4 - 1
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):

+ 2 - 2
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(),

+ 14 - 10
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')
 
 

+ 1 - 1
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',
         ]
 
 

+ 2 - 0
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')),
             ],

+ 20 - 8
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
         )
 
 

+ 6 - 0
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:

+ 1 - 1
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')
 
 

+ 1 - 1
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"
 
 

+ 4 - 0
netbox/templates/dcim/modulebay.html

@@ -23,6 +23,10 @@
               <th scope="row">Label</th>
               <td>{{ object.label|placeholder }}</td>
             </tr>
+            <tr>
+                <th scope="row">Position</th>
+                <td>{{ object.position|placeholder }}</td>
+            </tr>
             <tr>
               <th scope="row">Description</th>
               <td>{{ object.description|placeholder }}</td>

+ 1 - 1
netbox/templates/dcim/moduletype/base.html

@@ -45,7 +45,7 @@
 
 {% block tab_items %}
     <li role="presentation" class="nav-item">
-        <a href="{% url 'dcim:devicetype' pk=object.pk %}" class="nav-link{% if active_tab == 'moduletype' %} active{% endif %}">
+        <a href="{% url 'dcim:moduletype' pk=object.pk %}" class="nav-link{% if active_tab == 'moduletype' %} active{% endif %}">
             Module Type
         </a>
     </li>