1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Add position field for module bays

This commit is contained in:
jeremystretch
2021-12-20 09:51:55 -05:00
parent ed6a160372
commit eaa1165611
14 changed files with 65 additions and 31 deletions

View File

@ -432,7 +432,10 @@ class ModuleBayTemplateSerializer(ValidatedModelSerializer):
class Meta: class Meta:
model = ModuleBayTemplate 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): class DeviceBayTemplateSerializer(ValidatedModelSerializer):
@ -785,8 +788,8 @@ class ModuleBaySerializer(PrimaryModelSerializer):
class Meta: class Meta:
model = ModuleBay model = ModuleBay
fields = [ fields = [
'id', 'url', 'display', 'device', 'name', 'label', 'description', 'tags', 'custom_fields', 'created', 'id', 'url', 'display', 'device', 'name', 'label', 'position', 'description', 'tags', 'custom_fields',
'last_updated', 'created', 'last_updated',
] ]

View File

@ -886,7 +886,7 @@ class ModuleBayTemplateBulkEditForm(BulkEditForm):
) )
class Meta: class Meta:
nullable_fields = ('label', 'description') nullable_fields = ('label', 'position', 'description')
class DeviceBayTemplateBulkEditForm(BulkEditForm): class DeviceBayTemplateBulkEditForm(BulkEditForm):
@ -1153,7 +1153,7 @@ class ModuleBayBulkEditForm(
) )
class Meta: class Meta:
nullable_fields = ['label', 'description'] nullable_fields = ['label', 'position', 'description']
class DeviceBayBulkEditForm( class DeviceBayBulkEditForm(

View File

@ -717,7 +717,7 @@ class ModuleBayCSVForm(CustomFieldModelCSVForm):
class Meta: class Meta:
model = ModuleBay model = ModuleBay
fields = ('device', 'name', 'label', 'description') fields = ('device', 'name', 'label', 'position', 'description')
class DeviceBayCSVForm(CustomFieldModelCSVForm): class DeviceBayCSVForm(CustomFieldModelCSVForm):

View File

@ -1073,10 +1073,13 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
model = ModuleBay model = ModuleBay
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
['name', 'label'], ['name', 'label', 'position'],
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
] ]
tag = TagFilterField(model) tag = TagFilterField(model)
position = forms.CharField(
required=False
)
class DeviceBayFilterForm(DeviceComponentFilterForm): class DeviceBayFilterForm(DeviceComponentFilterForm):

View File

@ -1059,7 +1059,7 @@ class ModuleBayTemplateForm(BootstrapMixin, forms.ModelForm):
class Meta: class Meta:
model = ModuleBayTemplate model = ModuleBayTemplate
fields = [ fields = [
'device_type', 'name', 'label', 'description', 'device_type', 'name', 'label', 'position', 'description',
] ]
widgets = { widgets = {
'device_type': forms.HiddenInput(), 'device_type': forms.HiddenInput(),
@ -1313,7 +1313,7 @@ class ModuleBayForm(CustomFieldModelForm):
class Meta: class Meta:
model = ModuleBay model = ModuleBay
fields = [ fields = [
'device', 'name', 'label', 'description', 'tags', 'device', 'name', 'label', 'position', 'description', 'tags',
] ]
widgets = { widgets = {
'device': forms.HiddenInput(), 'device': forms.HiddenInput(),

View File

@ -163,6 +163,12 @@ class ComponentTemplateCreateForm(ComponentForm):
'manufacturer_id': '$manufacturer' 'manufacturer_id': '$manufacturer'
} }
) )
description = forms.CharField(
required=False
)
class ModularComponentTemplateCreateForm(ComponentTemplateCreateForm):
module_type = DynamicModelChoiceField( module_type = DynamicModelChoiceField(
queryset=ModuleType.objects.all(), queryset=ModuleType.objects.all(),
required=False, required=False,
@ -170,12 +176,9 @@ class ComponentTemplateCreateForm(ComponentForm):
'manufacturer_id': '$manufacturer' 'manufacturer_id': '$manufacturer'
} }
) )
description = forms.CharField(
required=False
)
class ConsolePortTemplateCreateForm(ComponentTemplateCreateForm): class ConsolePortTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=add_blank_choice(ConsolePortTypeChoices), choices=add_blank_choice(ConsolePortTypeChoices),
widget=StaticSelect() widget=StaticSelect()
@ -185,7 +188,7 @@ class ConsolePortTemplateCreateForm(ComponentTemplateCreateForm):
) )
class ConsoleServerPortTemplateCreateForm(ComponentTemplateCreateForm): class ConsoleServerPortTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=add_blank_choice(ConsolePortTypeChoices), choices=add_blank_choice(ConsolePortTypeChoices),
widget=StaticSelect() widget=StaticSelect()
@ -195,7 +198,7 @@ class ConsoleServerPortTemplateCreateForm(ComponentTemplateCreateForm):
) )
class PowerPortTemplateCreateForm(ComponentTemplateCreateForm): class PowerPortTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=add_blank_choice(PowerPortTypeChoices), choices=add_blank_choice(PowerPortTypeChoices),
required=False required=False
@ -216,7 +219,7 @@ class PowerPortTemplateCreateForm(ComponentTemplateCreateForm):
) )
class PowerOutletTemplateCreateForm(ComponentTemplateCreateForm): class PowerOutletTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=add_blank_choice(PowerOutletTypeChoices), choices=add_blank_choice(PowerOutletTypeChoices),
required=False required=False
@ -240,7 +243,7 @@ class PowerOutletTemplateCreateForm(ComponentTemplateCreateForm):
) )
class InterfaceTemplateCreateForm(ComponentTemplateCreateForm): class InterfaceTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=InterfaceTypeChoices, choices=InterfaceTypeChoices,
widget=StaticSelect() widget=StaticSelect()
@ -255,7 +258,7 @@ class InterfaceTemplateCreateForm(ComponentTemplateCreateForm):
) )
class FrontPortTemplateCreateForm(ComponentTemplateCreateForm): class FrontPortTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=PortTypeChoices, choices=PortTypeChoices,
widget=StaticSelect() widget=StaticSelect()
@ -320,7 +323,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm):
} }
class RearPortTemplateCreateForm(ComponentTemplateCreateForm): class RearPortTemplateCreateForm(ModularComponentTemplateCreateForm):
type = forms.ChoiceField( type = forms.ChoiceField(
choices=PortTypeChoices, choices=PortTypeChoices,
widget=StaticSelect(), widget=StaticSelect(),
@ -341,6 +344,7 @@ class RearPortTemplateCreateForm(ComponentTemplateCreateForm):
class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm): class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm):
# TODO: Support patterned position assignment
field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description') field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')

View File

@ -155,7 +155,7 @@ class ModuleBayTemplateImportForm(ComponentTemplateImportForm):
class Meta: class Meta:
model = ModuleBayTemplate model = ModuleBayTemplate
fields = [ fields = [
'device_type', 'name', 'label', 'description', 'device_type', 'name', 'label', 'position', 'description',
] ]

View File

@ -105,6 +105,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=64)), ('name', models.CharField(max_length=64)),
('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
('label', models.CharField(blank=True, max_length=64)), ('label', models.CharField(blank=True, max_length=64)),
('position', models.CharField(blank=True, max_length=30)),
('description', models.CharField(blank=True, max_length=200)), ('description', models.CharField(blank=True, max_length=200)),
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modulebays', to='dcim.device')), ('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')), ('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', models.CharField(max_length=64)),
('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
('label', models.CharField(blank=True, max_length=64)), ('label', models.CharField(blank=True, max_length=64)),
('position', models.CharField(blank=True, max_length=30)),
('description', models.CharField(blank=True, max_length=200)), ('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')), ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modulebaytemplates', to='dcim.devicetype')),
], ],

View File

@ -123,6 +123,11 @@ class ModularComponentTemplateModel(ComponentTemplateModel):
"A component template must be associated with either a device type or a module type." "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') @extras_features('webhooks')
class ConsolePortTemplate(ModularComponentTemplateModel): class ConsolePortTemplate(ModularComponentTemplateModel):
@ -144,7 +149,7 @@ class ConsolePortTemplate(ModularComponentTemplateModel):
def instantiate(self, **kwargs): def instantiate(self, **kwargs):
return ConsolePort( return ConsolePort(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
**kwargs **kwargs
@ -171,7 +176,7 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel):
def instantiate(self, **kwargs): def instantiate(self, **kwargs):
return ConsoleServerPort( return ConsoleServerPort(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
**kwargs **kwargs
@ -210,7 +215,7 @@ class PowerPortTemplate(ModularComponentTemplateModel):
def instantiate(self, **kwargs): def instantiate(self, **kwargs):
return PowerPort( return PowerPort(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
maximum_draw=self.maximum_draw, maximum_draw=self.maximum_draw,
@ -279,7 +284,7 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
else: else:
power_port = None power_port = None
return PowerOutlet( return PowerOutlet(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
power_port=power_port, power_port=power_port,
@ -318,7 +323,7 @@ class InterfaceTemplate(ModularComponentTemplateModel):
def instantiate(self, **kwargs): def instantiate(self, **kwargs):
return Interface( return Interface(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
mgmt_only=self.mgmt_only, mgmt_only=self.mgmt_only,
@ -387,7 +392,7 @@ class FrontPortTemplate(ModularComponentTemplateModel):
else: else:
rear_port = None rear_port = None
return FrontPort( return FrontPort(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
color=self.color, color=self.color,
@ -426,7 +431,7 @@ class RearPortTemplate(ModularComponentTemplateModel):
def instantiate(self, **kwargs): def instantiate(self, **kwargs):
return RearPort( return RearPort(
name=self.name, name=self.resolve_name(kwargs.get('module')),
label=self.label, label=self.label,
type=self.type, type=self.type,
color=self.color, color=self.color,
@ -440,6 +445,12 @@ class ModuleBayTemplate(ComponentTemplateModel):
""" """
A template for a ModuleBay to be created for a new parent Device. 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: class Meta:
ordering = ('device_type', '_name') ordering = ('device_type', '_name')
unique_together = ('device_type', 'name') unique_together = ('device_type', 'name')
@ -448,7 +459,8 @@ class ModuleBayTemplate(ComponentTemplateModel):
return ModuleBay( return ModuleBay(
device=device, device=device,
name=self.name, name=self.name,
label=self.label label=self.label,
position=self.position
) )

View File

@ -880,6 +880,12 @@ class ModuleBay(ComponentModel):
""" """
An empty space within a Device which can house a child device 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'] clone_fields = ['device']
class Meta: class Meta:

View File

@ -749,7 +749,7 @@ class ModuleBayTable(DeviceComponentTable):
class Meta(DeviceComponentTable.Meta): class Meta(DeviceComponentTable.Meta):
model = ModuleBay 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') default_columns = ('pk', 'name', 'device', 'label', 'installed_module', 'description')

View File

@ -217,7 +217,7 @@ class ModuleBayTemplateTable(ComponentTemplateTable):
class Meta(ComponentTemplateTable.Meta): class Meta(ComponentTemplateTable.Meta):
model = ModuleBayTemplate model = ModuleBayTemplate
fields = ('pk', 'name', 'label', 'description', 'actions') fields = ('pk', 'name', 'label', 'position', 'description', 'actions')
empty_text = "None" empty_text = "None"

View File

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

View File

@ -45,7 +45,7 @@
{% block tab_items %} {% block tab_items %}
<li role="presentation" class="nav-item"> <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 Module Type
</a> </a>
</li> </li>