Closes #8667: Support position patterning when creating module bays & templates

This commit is contained in:
jeremystretch 2022-02-23 13:02:14 -05:00
parent 6604ebfd01
commit a2fe23549b
5 changed files with 51 additions and 21 deletions

View File

@ -8,11 +8,13 @@ from utilities.forms import (
) )
__all__ = ( __all__ = (
'ModularComponentTemplateCreateForm',
'DeviceComponentCreateForm',
'ComponentTemplateCreateForm', 'ComponentTemplateCreateForm',
'DeviceComponentCreateForm',
'FrontPortCreateForm', 'FrontPortCreateForm',
'FrontPortTemplateCreateForm', 'FrontPortTemplateCreateForm',
'ModularComponentTemplateCreateForm',
'ModuleBayCreateForm',
'ModuleBayTemplateCreateForm',
'VirtualChassisCreateForm', 'VirtualChassisCreateForm',
) )
@ -34,14 +36,17 @@ class ComponentCreateForm(BootstrapMixin, forms.Form):
def clean(self): def clean(self):
super().clean() super().clean()
# Validate that the number of components being created from both the name_pattern and label_pattern are equal # Validate that all patterned fields generate an equal number of values
if self.cleaned_data['label_pattern']: patterned_fields = [
name_pattern_count = len(self.cleaned_data['name_pattern']) field_name for field_name in self.fields if field_name.endswith('_pattern')
label_pattern_count = len(self.cleaned_data['label_pattern']) ]
if name_pattern_count != label_pattern_count: pattern_count = len(self.cleaned_data['name_pattern'])
for field_name in patterned_fields:
value_count = len(self.cleaned_data[field_name])
if self.cleaned_data[field_name] and value_count != pattern_count:
raise forms.ValidationError({ raise forms.ValidationError({
'label_pattern': f'The provided name pattern will create {name_pattern_count} components, however ' field_name: f'The provided pattern specifies {value_count} values, but {pattern_count} are '
f'{label_pattern_count} labels will be generated. These counts must match.' f'expected.'
}, code='label_pattern_mismatch') }, code='label_pattern_mismatch')
@ -176,6 +181,24 @@ class FrontPortCreateForm(DeviceComponentCreateForm):
} }
class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm):
position_pattern = ExpandableNameField(
label='Position',
required=False,
help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)'
)
field_order = ('device_type', 'name_pattern', 'label_pattern', 'position_pattern')
class ModuleBayCreateForm(DeviceComponentCreateForm):
position_pattern = ExpandableNameField(
label='Position',
required=False,
help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)'
)
field_order = ('device', 'name_pattern', 'label_pattern', 'position_pattern')
class VirtualChassisCreateForm(NetBoxModelForm): class VirtualChassisCreateForm(NetBoxModelForm):
region = DynamicModelChoiceField( region = DynamicModelChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),

View File

@ -744,8 +744,8 @@ class DeviceModuleBayTable(ModuleBayTable):
class Meta(DeviceComponentTable.Meta): class Meta(DeviceComponentTable.Meta):
model = ModuleBay model = ModuleBay
fields = ('pk', 'id', 'name', 'label', 'description', 'installed_module', 'tags', 'actions') fields = ('pk', 'id', 'name', 'label', 'position', 'installed_module', 'description', 'tags', 'actions')
default_columns = ('pk', 'name', 'label', 'description', 'installed_module') default_columns = ('pk', 'name', 'label', 'installed_module', 'description')
class InventoryItemTable(DeviceComponentTable): class InventoryItemTable(DeviceComponentTable):

View File

@ -1324,9 +1324,10 @@ class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
class ModuleBayTemplateCreateView(generic.ComponentCreateView): class ModuleBayTemplateCreateView(generic.ComponentCreateView):
queryset = ModuleBayTemplate.objects.all() queryset = ModuleBayTemplate.objects.all()
form = forms.ComponentTemplateCreateForm form = forms.ModuleBayTemplateCreateForm
model_form = forms.ModuleBayTemplateForm model_form = forms.ModuleBayTemplateForm
template_name = 'dcim/component_template_create.html' template_name = 'dcim/modulebaytemplate_create.html'
patterned_fields = ('name', 'label', 'position')
class ModuleBayTemplateEditView(generic.ObjectEditView): class ModuleBayTemplateEditView(generic.ObjectEditView):
@ -2304,8 +2305,9 @@ class ModuleBayView(generic.ObjectView):
class ModuleBayCreateView(generic.ComponentCreateView): class ModuleBayCreateView(generic.ComponentCreateView):
queryset = ModuleBay.objects.all() queryset = ModuleBay.objects.all()
form = forms.DeviceComponentCreateForm form = forms.ModuleBayCreateForm
model_form = forms.ModuleBayForm model_form = forms.ModuleBayForm
patterned_fields = ('name', 'label', 'position')
class ModuleBayEditView(generic.ObjectEditView): class ModuleBayEditView(generic.ObjectEditView):

View File

@ -587,14 +587,12 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView):
if form.is_valid(): if form.is_valid():
new_components = [] new_components = []
data = deepcopy(request.POST) data = deepcopy(request.POST)
names = form.cleaned_data['name_pattern'] pattern_count = len(form.cleaned_data[f'{self.patterned_fields[0]}_pattern'])
labels = form.cleaned_data.get('label_pattern')
for i, name in enumerate(names): for i in range(pattern_count):
label = labels[i] if labels else None for field_name in self.patterned_fields:
# Initialize the individual component form if form.cleaned_data.get(f'{field_name}_pattern'):
data['name'] = name data[field_name] = form.cleaned_data[f'{field_name}_pattern'][i]
data['label'] = label
if hasattr(form, 'get_iterative_data'): if hasattr(form, 'get_iterative_data'):
data.update(form.get_iterative_data(i)) data.update(form.get_iterative_data(i))

View File

@ -0,0 +1,7 @@
{% extends 'dcim/component_template_create.html' %}
{% load form_helpers %}
{% block replication_fields %}
{{ block.super }}
{% render_field replication_form.position_pattern %}
{% endblock replication_fields %}