mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Refactor ComponentCreateView to use separate forms for names/labels and model creation
This commit is contained in:
parent
99d5013de3
commit
a237c01b4b
@ -4,7 +4,7 @@ from dcim.models import *
|
|||||||
from extras.forms import CustomFieldsMixin
|
from extras.forms import CustomFieldsMixin
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from utilities.forms import DynamicModelMultipleChoiceField, form_from_model
|
from utilities.forms import DynamicModelMultipleChoiceField, form_from_model
|
||||||
from .object_create import ComponentForm
|
from .object_create import ComponentCreateForm
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ConsolePortBulkCreateForm',
|
'ConsolePortBulkCreateForm',
|
||||||
@ -24,7 +24,7 @@ __all__ = (
|
|||||||
# Device components
|
# Device components
|
||||||
#
|
#
|
||||||
|
|
||||||
class DeviceBulkAddComponentForm(CustomFieldsMixin, ComponentForm):
|
class DeviceBulkAddComponentForm(CustomFieldsMixin, ComponentCreateForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
|
@ -1,44 +1,19 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
|
|
||||||
from dcim.choices import *
|
|
||||||
from dcim.constants import *
|
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelForm, CustomFieldsMixin
|
from extras.forms import CustomFieldModelForm
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from ipam.models import VLAN
|
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, BootstrapMixin, ColorField, ContentTypeChoiceField, DynamicModelChoiceField,
|
BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField,
|
||||||
DynamicModelMultipleChoiceField, ExpandableNameField, StaticSelect,
|
|
||||||
)
|
)
|
||||||
from wireless.choices import *
|
|
||||||
from .common import InterfaceCommonForm
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ConsolePortCreateForm',
|
'ComponentCreateForm',
|
||||||
'ConsolePortTemplateCreateForm',
|
|
||||||
'ConsoleServerPortCreateForm',
|
|
||||||
'ConsoleServerPortTemplateCreateForm',
|
|
||||||
'DeviceBayCreateForm',
|
|
||||||
'DeviceBayTemplateCreateForm',
|
|
||||||
'FrontPortCreateForm',
|
|
||||||
'FrontPortTemplateCreateForm',
|
|
||||||
'InterfaceCreateForm',
|
|
||||||
'InterfaceTemplateCreateForm',
|
|
||||||
'InventoryItemCreateForm',
|
|
||||||
'ModuleBayCreateForm',
|
|
||||||
'ModuleBayTemplateCreateForm',
|
|
||||||
'PowerOutletCreateForm',
|
|
||||||
'PowerOutletTemplateCreateForm',
|
|
||||||
'PowerPortCreateForm',
|
|
||||||
'PowerPortTemplateCreateForm',
|
|
||||||
'RearPortCreateForm',
|
|
||||||
'RearPortTemplateCreateForm',
|
|
||||||
'VirtualChassisCreateForm',
|
'VirtualChassisCreateForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ComponentForm(BootstrapMixin, forms.Form):
|
class ComponentCreateForm(BootstrapMixin, forms.Form):
|
||||||
"""
|
"""
|
||||||
Subclass this form when facilitating the creation of one or more device component or component templates based on
|
Subclass this form when facilitating the creation of one or more device component or component templates based on
|
||||||
a name pattern.
|
a name pattern.
|
||||||
@ -139,558 +114,3 @@ class VirtualChassisCreateForm(CustomFieldModelForm):
|
|||||||
member.save()
|
member.save()
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Component templates
|
|
||||||
#
|
|
||||||
|
|
||||||
class ComponentTemplateCreateForm(ComponentForm):
|
|
||||||
"""
|
|
||||||
Base form for the creation of device component templates (subclassed from ComponentTemplateModel).
|
|
||||||
"""
|
|
||||||
manufacturer = DynamicModelChoiceField(
|
|
||||||
queryset=Manufacturer.objects.all(),
|
|
||||||
required=False,
|
|
||||||
initial_params={
|
|
||||||
'device_types': 'device_type',
|
|
||||||
'module_types': 'module_type',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
device_type = DynamicModelChoiceField(
|
|
||||||
queryset=DeviceType.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'manufacturer_id': '$manufacturer'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
description = forms.CharField(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ModularComponentTemplateCreateForm(ComponentTemplateCreateForm):
|
|
||||||
module_type = DynamicModelChoiceField(
|
|
||||||
queryset=ModuleType.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'manufacturer_id': '$manufacturer'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(ConsolePortTypeChoices),
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(ConsolePortTypeChoices),
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(PowerPortTypeChoices),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
maximum_draw = forms.IntegerField(
|
|
||||||
min_value=1,
|
|
||||||
required=False,
|
|
||||||
help_text="Maximum power draw (watts)"
|
|
||||||
)
|
|
||||||
allocated_draw = forms.IntegerField(
|
|
||||||
min_value=1,
|
|
||||||
required=False,
|
|
||||||
help_text="Allocated power draw (watts)"
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'maximum_draw',
|
|
||||||
'allocated_draw', 'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(PowerOutletTypeChoices),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
power_port = DynamicModelChoiceField(
|
|
||||||
queryset=PowerPortTemplate.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'devicetype_id': '$device_type',
|
|
||||||
'moduletype_id': '$module_type',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
feed_leg = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(PowerOutletFeedLegChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'power_port', 'feed_leg',
|
|
||||||
'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=InterfaceTypeChoices,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
mgmt_only = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
label='Management only'
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'mgmt_only',
|
|
||||||
'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=PortTypeChoices,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
color = ColorField(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
rear_port_set = forms.MultipleChoiceField(
|
|
||||||
choices=[],
|
|
||||||
label='Rear ports',
|
|
||||||
help_text='Select one rear port assignment for each front port being created.',
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'color', 'rear_port_set',
|
|
||||||
'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
device_type = DeviceType.objects.get(
|
|
||||||
pk=self.initial.get('device_type') or self.data.get('device_type')
|
|
||||||
)
|
|
||||||
|
|
||||||
# Determine which rear port positions are occupied. These will be excluded from the list of available mappings.
|
|
||||||
occupied_port_positions = [
|
|
||||||
(front_port.rear_port_id, front_port.rear_port_position)
|
|
||||||
for front_port in device_type.frontporttemplates.all()
|
|
||||||
]
|
|
||||||
|
|
||||||
# Populate rear port choices
|
|
||||||
choices = []
|
|
||||||
rear_ports = RearPortTemplate.objects.filter(device_type=device_type)
|
|
||||||
for rear_port in rear_ports:
|
|
||||||
for i in range(1, rear_port.positions + 1):
|
|
||||||
if (rear_port.pk, i) not in occupied_port_positions:
|
|
||||||
choices.append(
|
|
||||||
('{}:{}'.format(rear_port.pk, i), '{}:{}'.format(rear_port.name, i))
|
|
||||||
)
|
|
||||||
self.fields['rear_port_set'].choices = choices
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
# Validate that the number of ports being created equals the number of selected (rear port, position) tuples
|
|
||||||
front_port_count = len(self.cleaned_data['name_pattern'])
|
|
||||||
rear_port_count = len(self.cleaned_data['rear_port_set'])
|
|
||||||
if front_port_count != rear_port_count:
|
|
||||||
raise forms.ValidationError({
|
|
||||||
'rear_port_set': 'The provided name pattern will create {} ports, however {} rear port assignments '
|
|
||||||
'were selected. These counts must match.'.format(front_port_count, rear_port_count)
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_iterative_data(self, iteration):
|
|
||||||
|
|
||||||
# Assign rear port and position from selected set
|
|
||||||
rear_port, position = self.cleaned_data['rear_port_set'][iteration].split(':')
|
|
||||||
|
|
||||||
return {
|
|
||||||
'rear_port': int(rear_port),
|
|
||||||
'rear_port_position': int(position),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateCreateForm(ModularComponentTemplateCreateForm):
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=PortTypeChoices,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
)
|
|
||||||
color = ColorField(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
positions = forms.IntegerField(
|
|
||||||
min_value=REARPORT_POSITIONS_MIN,
|
|
||||||
max_value=REARPORT_POSITIONS_MAX,
|
|
||||||
initial=1,
|
|
||||||
help_text='The number of front ports which may be mapped to each rear port'
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'manufacturer', 'device_type', 'module_type', 'name_pattern', 'label_pattern', 'type', 'color', 'positions',
|
|
||||||
'description',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm):
|
|
||||||
# TODO: Support patterned position assignment
|
|
||||||
field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateCreateForm(ComponentTemplateCreateForm):
|
|
||||||
field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Device components
|
|
||||||
#
|
|
||||||
|
|
||||||
class ComponentCreateForm(CustomFieldsMixin, ComponentForm):
|
|
||||||
"""
|
|
||||||
Base form for the creation of device components (models subclassed from ComponentModel).
|
|
||||||
"""
|
|
||||||
device = DynamicModelChoiceField(
|
|
||||||
queryset=Device.objects.all()
|
|
||||||
)
|
|
||||||
description = forms.CharField(
|
|
||||||
max_length=200,
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
tags = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Tag.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortCreateForm(ComponentCreateForm):
|
|
||||||
model = ConsolePort
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(ConsolePortTypeChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
speed = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(ConsolePortSpeedChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'speed', 'mark_connected', 'description', 'tags')
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortCreateForm(ComponentCreateForm):
|
|
||||||
model = ConsoleServerPort
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(ConsolePortTypeChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
speed = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(ConsolePortSpeedChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
field_order = ('device', 'name_pattern', 'label_pattern', 'type', 'speed', 'mark_connected', 'description', 'tags')
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPortCreateForm(ComponentCreateForm):
|
|
||||||
model = PowerPort
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(PowerPortTypeChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
maximum_draw = forms.IntegerField(
|
|
||||||
min_value=1,
|
|
||||||
required=False,
|
|
||||||
help_text="Maximum draw in watts"
|
|
||||||
)
|
|
||||||
allocated_draw = forms.IntegerField(
|
|
||||||
min_value=1,
|
|
||||||
required=False,
|
|
||||||
help_text="Allocated draw in watts"
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'device', 'name_pattern', 'label_pattern', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected',
|
|
||||||
'description', 'tags',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletCreateForm(ComponentCreateForm):
|
|
||||||
model = PowerOutlet
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(PowerOutletTypeChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
power_port = forms.ModelChoiceField(
|
|
||||||
queryset=PowerPort.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
feed_leg = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(PowerOutletFeedLegChoices),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'device', 'name_pattern', 'label_pattern', 'type', 'power_port', 'feed_leg', 'mark_connected', 'description',
|
|
||||||
'tags',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Limit power_port queryset to PowerPorts which belong to the parent Device
|
|
||||||
device = Device.objects.get(
|
|
||||||
pk=self.initial.get('device') or self.data.get('device')
|
|
||||||
)
|
|
||||||
self.fields['power_port'].queryset = PowerPort.objects.filter(device=device)
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
|
|
||||||
model = Interface
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=InterfaceTypeChoices,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
)
|
|
||||||
enabled = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
initial=True
|
|
||||||
)
|
|
||||||
parent = DynamicModelChoiceField(
|
|
||||||
queryset=Interface.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'device_id': '$device',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
bridge = DynamicModelChoiceField(
|
|
||||||
queryset=Interface.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'device_id': '$device',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
lag = DynamicModelChoiceField(
|
|
||||||
queryset=Interface.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'device_id': '$device',
|
|
||||||
'type': 'lag',
|
|
||||||
},
|
|
||||||
label='LAG'
|
|
||||||
)
|
|
||||||
mac_address = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label='MAC Address'
|
|
||||||
)
|
|
||||||
wwn = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label='WWN'
|
|
||||||
)
|
|
||||||
mgmt_only = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
label='Management only',
|
|
||||||
help_text='This interface is used only for out-of-band management'
|
|
||||||
)
|
|
||||||
mode = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(InterfaceModeChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect()
|
|
||||||
)
|
|
||||||
rf_role = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(WirelessRoleChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
label='Wireless role'
|
|
||||||
)
|
|
||||||
rf_channel = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(WirelessChannelChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
label='Wireless channel'
|
|
||||||
)
|
|
||||||
rf_channel_frequency = forms.DecimalField(
|
|
||||||
required=False,
|
|
||||||
label='Channel frequency (MHz)'
|
|
||||||
)
|
|
||||||
rf_channel_width = forms.DecimalField(
|
|
||||||
required=False,
|
|
||||||
label='Channel width (MHz)'
|
|
||||||
)
|
|
||||||
untagged_vlan = DynamicModelChoiceField(
|
|
||||||
queryset=VLAN.objects.all(),
|
|
||||||
required=False,
|
|
||||||
label='Untagged VLAN'
|
|
||||||
)
|
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=VLAN.objects.all(),
|
|
||||||
required=False,
|
|
||||||
label='Tagged VLANs'
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'device', 'name_pattern', 'label_pattern', 'type', 'enabled', 'parent', 'bridge', 'lag', 'mtu', 'mac_address',
|
|
||||||
'wwn', 'description', 'mgmt_only', 'mark_connected', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
|
||||||
'rf_channel_width', 'mode', 'untagged_vlan', 'tagged_vlans', 'tags'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Limit VLAN choices by device
|
|
||||||
device_id = self.initial.get('device') or self.data.get('device')
|
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device_id)
|
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device_id)
|
|
||||||
|
|
||||||
|
|
||||||
class FrontPortCreateForm(ComponentCreateForm):
|
|
||||||
model = FrontPort
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=PortTypeChoices,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
)
|
|
||||||
color = ColorField(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
rear_port_set = forms.MultipleChoiceField(
|
|
||||||
choices=[],
|
|
||||||
label='Rear ports',
|
|
||||||
help_text='Select one rear port assignment for each front port being created.',
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'device', 'name_pattern', 'label_pattern', 'type', 'color', 'rear_port_set', 'mark_connected', 'description',
|
|
||||||
'tags',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
device = Device.objects.get(
|
|
||||||
pk=self.initial.get('device') or self.data.get('device')
|
|
||||||
)
|
|
||||||
|
|
||||||
# Determine which rear port positions are occupied. These will be excluded from the list of available
|
|
||||||
# mappings.
|
|
||||||
occupied_port_positions = [
|
|
||||||
(front_port.rear_port_id, front_port.rear_port_position)
|
|
||||||
for front_port in device.frontports.all()
|
|
||||||
]
|
|
||||||
|
|
||||||
# Populate rear port choices
|
|
||||||
choices = []
|
|
||||||
rear_ports = RearPort.objects.filter(device=device)
|
|
||||||
for rear_port in rear_ports:
|
|
||||||
for i in range(1, rear_port.positions + 1):
|
|
||||||
if (rear_port.pk, i) not in occupied_port_positions:
|
|
||||||
choices.append(
|
|
||||||
('{}:{}'.format(rear_port.pk, i), '{}:{}'.format(rear_port.name, i))
|
|
||||||
)
|
|
||||||
self.fields['rear_port_set'].choices = choices
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
# Validate that the number of ports being created equals the number of selected (rear port, position) tuples
|
|
||||||
front_port_count = len(self.cleaned_data['name_pattern'])
|
|
||||||
rear_port_count = len(self.cleaned_data['rear_port_set'])
|
|
||||||
if front_port_count != rear_port_count:
|
|
||||||
raise forms.ValidationError({
|
|
||||||
'rear_port_set': 'The provided name pattern will create {} ports, however {} rear port assignments '
|
|
||||||
'were selected. These counts must match.'.format(front_port_count, rear_port_count)
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_iterative_data(self, iteration):
|
|
||||||
|
|
||||||
# Assign rear port and position from selected set
|
|
||||||
rear_port, position = self.cleaned_data['rear_port_set'][iteration].split(':')
|
|
||||||
|
|
||||||
return {
|
|
||||||
'rear_port': int(rear_port),
|
|
||||||
'rear_port_position': int(position),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortCreateForm(ComponentCreateForm):
|
|
||||||
model = RearPort
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
choices=PortTypeChoices,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
)
|
|
||||||
color = ColorField(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
positions = forms.IntegerField(
|
|
||||||
min_value=REARPORT_POSITIONS_MIN,
|
|
||||||
max_value=REARPORT_POSITIONS_MAX,
|
|
||||||
initial=1,
|
|
||||||
help_text='The number of front ports which may be mapped to each rear port'
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'device', 'name_pattern', 'label_pattern', 'type', 'color', 'positions', 'mark_connected', 'description',
|
|
||||||
'tags',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayCreateForm(ComponentCreateForm):
|
|
||||||
model = ModuleBay
|
|
||||||
field_order = ('device', 'name_pattern', 'label_pattern', 'description', 'tags')
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayCreateForm(ComponentCreateForm):
|
|
||||||
model = DeviceBay
|
|
||||||
field_order = ('device', 'name_pattern', 'label_pattern', 'description', 'tags')
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemCreateForm(ComponentCreateForm):
|
|
||||||
model = InventoryItem
|
|
||||||
parent = DynamicModelChoiceField(
|
|
||||||
queryset=InventoryItem.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'device_id': '$device'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
role = DynamicModelChoiceField(
|
|
||||||
queryset=InventoryItemRole.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
manufacturer = DynamicModelChoiceField(
|
|
||||||
queryset=Manufacturer.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
part_id = forms.CharField(
|
|
||||||
max_length=50,
|
|
||||||
required=False,
|
|
||||||
label='Part ID'
|
|
||||||
)
|
|
||||||
serial = forms.CharField(
|
|
||||||
max_length=50,
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
asset_tag = forms.CharField(
|
|
||||||
max_length=50,
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
component_type = ContentTypeChoiceField(
|
|
||||||
queryset=ContentType.objects.all(),
|
|
||||||
limit_choices_to=MODULAR_COMPONENT_MODELS,
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect
|
|
||||||
)
|
|
||||||
component_id = forms.IntegerField(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'device', 'parent', 'name_pattern', 'label_pattern', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
|
||||||
'description', 'component_type', 'component_id', 'tags',
|
|
||||||
)
|
|
||||||
|
@ -118,41 +118,27 @@ class DeviceTestCase(TestCase):
|
|||||||
|
|
||||||
class LabelTestCase(TestCase):
|
class LabelTestCase(TestCase):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpTestData(cls):
|
|
||||||
site = Site.objects.create(name='Site 2', slug='site-2')
|
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 2', slug='manufacturer-2')
|
|
||||||
cls.device_type = DeviceType.objects.create(
|
|
||||||
manufacturer=manufacturer, model='Device Type 2', slug='device-type-2', u_height=1
|
|
||||||
)
|
|
||||||
device_role = DeviceRole.objects.create(
|
|
||||||
name='Device Role 2', slug='device-role-2', color='ffff00'
|
|
||||||
)
|
|
||||||
cls.device = Device.objects.create(
|
|
||||||
name='Device 2', device_type=cls.device_type, device_role=device_role, site=site
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_interface_label_count_valid(self):
|
def test_interface_label_count_valid(self):
|
||||||
"""Test that a `label` can be generated for each generated `name` from `name_pattern` on InterfaceCreateForm"""
|
"""
|
||||||
|
Test that generating an equal number of names and labels passes form validation.
|
||||||
|
"""
|
||||||
interface_data = {
|
interface_data = {
|
||||||
'device': self.device.pk,
|
|
||||||
'name_pattern': 'eth[0-9]',
|
'name_pattern': 'eth[0-9]',
|
||||||
'label_pattern': 'Interface[0-9]',
|
'label_pattern': 'Interface[0-9]',
|
||||||
'type': InterfaceTypeChoices.TYPE_100ME_FIXED,
|
|
||||||
}
|
}
|
||||||
form = InterfaceCreateForm(interface_data)
|
form = ComponentCreateForm(interface_data)
|
||||||
|
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
def test_interface_label_count_mismatch(self):
|
def test_interface_label_count_mismatch(self):
|
||||||
"""Test that a `label` cannot be generated for each generated `name` from `name_pattern` due to invalid `label_pattern` on InterfaceCreateForm"""
|
"""
|
||||||
|
Check that attempting to generate a differing number of names and labels results in a validation error.
|
||||||
|
"""
|
||||||
bad_interface_data = {
|
bad_interface_data = {
|
||||||
'device': self.device.pk,
|
|
||||||
'name_pattern': 'eth[0-9]',
|
'name_pattern': 'eth[0-9]',
|
||||||
'label_pattern': 'Interface[0-1]',
|
'label_pattern': 'Interface[0-1]',
|
||||||
'type': InterfaceTypeChoices.TYPE_100ME_FIXED,
|
|
||||||
}
|
}
|
||||||
form = InterfaceCreateForm(bad_interface_data)
|
form = ComponentCreateForm(bad_interface_data)
|
||||||
|
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertIn('label_pattern', form.errors)
|
self.assertIn('label_pattern', form.errors)
|
||||||
|
@ -1054,7 +1054,6 @@ class ModuleTypeBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class ConsolePortTemplateCreateView(generic.ComponentCreateView):
|
class ConsolePortTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = ConsolePortTemplate.objects.all()
|
queryset = ConsolePortTemplate.objects.all()
|
||||||
form = forms.ConsolePortTemplateCreateForm
|
|
||||||
model_form = forms.ConsolePortTemplateForm
|
model_form = forms.ConsolePortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1088,7 +1087,6 @@ class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
|
class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = ConsoleServerPortTemplate.objects.all()
|
queryset = ConsoleServerPortTemplate.objects.all()
|
||||||
form = forms.ConsoleServerPortTemplateCreateForm
|
|
||||||
model_form = forms.ConsoleServerPortTemplateForm
|
model_form = forms.ConsoleServerPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1122,7 +1120,6 @@ class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class PowerPortTemplateCreateView(generic.ComponentCreateView):
|
class PowerPortTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = PowerPortTemplate.objects.all()
|
queryset = PowerPortTemplate.objects.all()
|
||||||
form = forms.PowerPortTemplateCreateForm
|
|
||||||
model_form = forms.PowerPortTemplateForm
|
model_form = forms.PowerPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1156,7 +1153,6 @@ class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class PowerOutletTemplateCreateView(generic.ComponentCreateView):
|
class PowerOutletTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = PowerOutletTemplate.objects.all()
|
queryset = PowerOutletTemplate.objects.all()
|
||||||
form = forms.PowerOutletTemplateCreateForm
|
|
||||||
model_form = forms.PowerOutletTemplateForm
|
model_form = forms.PowerOutletTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1190,7 +1186,6 @@ class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class InterfaceTemplateCreateView(generic.ComponentCreateView):
|
class InterfaceTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = InterfaceTemplate.objects.all()
|
queryset = InterfaceTemplate.objects.all()
|
||||||
form = forms.InterfaceTemplateCreateForm
|
|
||||||
model_form = forms.InterfaceTemplateForm
|
model_form = forms.InterfaceTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1224,7 +1219,6 @@ class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class FrontPortTemplateCreateView(generic.ComponentCreateView):
|
class FrontPortTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = FrontPortTemplate.objects.all()
|
queryset = FrontPortTemplate.objects.all()
|
||||||
form = forms.FrontPortTemplateCreateForm
|
|
||||||
model_form = forms.FrontPortTemplateForm
|
model_form = forms.FrontPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1258,7 +1252,6 @@ class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class RearPortTemplateCreateView(generic.ComponentCreateView):
|
class RearPortTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = RearPortTemplate.objects.all()
|
queryset = RearPortTemplate.objects.all()
|
||||||
form = forms.RearPortTemplateCreateForm
|
|
||||||
model_form = forms.RearPortTemplateForm
|
model_form = forms.RearPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1292,7 +1285,6 @@ class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class ModuleBayTemplateCreateView(generic.ComponentCreateView):
|
class ModuleBayTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = ModuleBayTemplate.objects.all()
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
form = forms.ModuleBayTemplateCreateForm
|
|
||||||
model_form = forms.ModuleBayTemplateForm
|
model_form = forms.ModuleBayTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1326,7 +1318,6 @@ class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class DeviceBayTemplateCreateView(generic.ComponentCreateView):
|
class DeviceBayTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = DeviceBayTemplate.objects.all()
|
queryset = DeviceBayTemplate.objects.all()
|
||||||
form = forms.DeviceBayTemplateCreateForm
|
|
||||||
model_form = forms.DeviceBayTemplateForm
|
model_form = forms.DeviceBayTemplateForm
|
||||||
|
|
||||||
|
|
||||||
@ -1741,7 +1732,6 @@ class ConsolePortView(generic.ObjectView):
|
|||||||
|
|
||||||
class ConsolePortCreateView(generic.ComponentCreateView):
|
class ConsolePortCreateView(generic.ComponentCreateView):
|
||||||
queryset = ConsolePort.objects.all()
|
queryset = ConsolePort.objects.all()
|
||||||
form = forms.ConsolePortCreateForm
|
|
||||||
model_form = forms.ConsolePortForm
|
model_form = forms.ConsolePortForm
|
||||||
|
|
||||||
|
|
||||||
@ -1800,7 +1790,6 @@ class ConsoleServerPortView(generic.ObjectView):
|
|||||||
|
|
||||||
class ConsoleServerPortCreateView(generic.ComponentCreateView):
|
class ConsoleServerPortCreateView(generic.ComponentCreateView):
|
||||||
queryset = ConsoleServerPort.objects.all()
|
queryset = ConsoleServerPort.objects.all()
|
||||||
form = forms.ConsoleServerPortCreateForm
|
|
||||||
model_form = forms.ConsoleServerPortForm
|
model_form = forms.ConsoleServerPortForm
|
||||||
|
|
||||||
|
|
||||||
@ -1859,7 +1848,6 @@ class PowerPortView(generic.ObjectView):
|
|||||||
|
|
||||||
class PowerPortCreateView(generic.ComponentCreateView):
|
class PowerPortCreateView(generic.ComponentCreateView):
|
||||||
queryset = PowerPort.objects.all()
|
queryset = PowerPort.objects.all()
|
||||||
form = forms.PowerPortCreateForm
|
|
||||||
model_form = forms.PowerPortForm
|
model_form = forms.PowerPortForm
|
||||||
|
|
||||||
|
|
||||||
@ -1918,7 +1906,6 @@ class PowerOutletView(generic.ObjectView):
|
|||||||
|
|
||||||
class PowerOutletCreateView(generic.ComponentCreateView):
|
class PowerOutletCreateView(generic.ComponentCreateView):
|
||||||
queryset = PowerOutlet.objects.all()
|
queryset = PowerOutlet.objects.all()
|
||||||
form = forms.PowerOutletCreateForm
|
|
||||||
model_form = forms.PowerOutletForm
|
model_form = forms.PowerOutletForm
|
||||||
|
|
||||||
|
|
||||||
@ -2012,7 +1999,6 @@ class InterfaceView(generic.ObjectView):
|
|||||||
|
|
||||||
class InterfaceCreateView(generic.ComponentCreateView):
|
class InterfaceCreateView(generic.ComponentCreateView):
|
||||||
queryset = Interface.objects.all()
|
queryset = Interface.objects.all()
|
||||||
form = forms.InterfaceCreateForm
|
|
||||||
model_form = forms.InterfaceForm
|
model_form = forms.InterfaceForm
|
||||||
template_name = 'dcim/interface_create.html'
|
template_name = 'dcim/interface_create.html'
|
||||||
|
|
||||||
@ -2098,7 +2084,6 @@ class FrontPortView(generic.ObjectView):
|
|||||||
|
|
||||||
class FrontPortCreateView(generic.ComponentCreateView):
|
class FrontPortCreateView(generic.ComponentCreateView):
|
||||||
queryset = FrontPort.objects.all()
|
queryset = FrontPort.objects.all()
|
||||||
form = forms.FrontPortCreateForm
|
|
||||||
model_form = forms.FrontPortForm
|
model_form = forms.FrontPortForm
|
||||||
|
|
||||||
|
|
||||||
@ -2157,7 +2142,6 @@ class RearPortView(generic.ObjectView):
|
|||||||
|
|
||||||
class RearPortCreateView(generic.ComponentCreateView):
|
class RearPortCreateView(generic.ComponentCreateView):
|
||||||
queryset = RearPort.objects.all()
|
queryset = RearPort.objects.all()
|
||||||
form = forms.RearPortCreateForm
|
|
||||||
model_form = forms.RearPortForm
|
model_form = forms.RearPortForm
|
||||||
|
|
||||||
|
|
||||||
@ -2216,7 +2200,6 @@ class ModuleBayView(generic.ObjectView):
|
|||||||
|
|
||||||
class ModuleBayCreateView(generic.ComponentCreateView):
|
class ModuleBayCreateView(generic.ComponentCreateView):
|
||||||
queryset = ModuleBay.objects.all()
|
queryset = ModuleBay.objects.all()
|
||||||
form = forms.ModuleBayCreateForm
|
|
||||||
model_form = forms.ModuleBayForm
|
model_form = forms.ModuleBayForm
|
||||||
|
|
||||||
|
|
||||||
@ -2271,7 +2254,6 @@ class DeviceBayView(generic.ObjectView):
|
|||||||
|
|
||||||
class DeviceBayCreateView(generic.ComponentCreateView):
|
class DeviceBayCreateView(generic.ComponentCreateView):
|
||||||
queryset = DeviceBay.objects.all()
|
queryset = DeviceBay.objects.all()
|
||||||
form = forms.DeviceBayCreateForm
|
|
||||||
model_form = forms.DeviceBayForm
|
model_form = forms.DeviceBayForm
|
||||||
|
|
||||||
|
|
||||||
@ -2397,7 +2379,6 @@ class InventoryItemEditView(generic.ObjectEditView):
|
|||||||
|
|
||||||
class InventoryItemCreateView(generic.ComponentCreateView):
|
class InventoryItemCreateView(generic.ComponentCreateView):
|
||||||
queryset = InventoryItem.objects.all()
|
queryset = InventoryItem.objects.all()
|
||||||
form = forms.InventoryItemCreateForm
|
|
||||||
model_form = forms.InventoryItemForm
|
model_form = forms.InventoryItemForm
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
|
from django.forms.widgets import HiddenInput
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
@ -14,6 +15,7 @@ from django.utils.safestring import mark_safe
|
|||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django_tables2.export import TableExport
|
from django_tables2.export import TableExport
|
||||||
|
|
||||||
|
from dcim.forms.object_create import ComponentCreateForm
|
||||||
from extras.models import ExportTemplate
|
from extras.models import ExportTemplate
|
||||||
from extras.signals import clear_webhooks
|
from extras.signals import clear_webhooks
|
||||||
from utilities.error_handlers import handle_protectederror
|
from utilities.error_handlers import handle_protectederror
|
||||||
@ -674,33 +676,45 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|||||||
# Device/VirtualMachine components
|
# Device/VirtualMachine components
|
||||||
#
|
#
|
||||||
|
|
||||||
# TODO: Replace with BulkCreateView
|
|
||||||
class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
||||||
"""
|
"""
|
||||||
Add one or more components (e.g. interfaces, console ports, etc.) to a Device or VirtualMachine.
|
Add one or more components (e.g. interfaces, console ports, etc.) to a Device or VirtualMachine.
|
||||||
"""
|
"""
|
||||||
queryset = None
|
queryset = None
|
||||||
form = None
|
form = ComponentCreateForm
|
||||||
model_form = None
|
model_form = None
|
||||||
template_name = 'generic/object_edit.html'
|
template_name = 'dcim/component_create.html'
|
||||||
|
patterned_fields = ('name', 'label')
|
||||||
|
|
||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return get_permission_for_model(self.queryset.model, 'add')
|
return get_permission_for_model(self.queryset.model, 'add')
|
||||||
|
|
||||||
def get(self, request):
|
def initialize_forms(self, request):
|
||||||
|
data = request.POST if request.method == 'POST' else None
|
||||||
|
initial_data = normalize_querydict(request.GET)
|
||||||
|
|
||||||
form = self.form(initial=request.GET)
|
form = self.form(data=data, initial=request.GET)
|
||||||
|
model_form = self.model_form(data=data, initial=initial_data)
|
||||||
|
|
||||||
|
# These fields will be set from the pattern values
|
||||||
|
for field_name in self.patterned_fields:
|
||||||
|
model_form.fields[field_name].widget = HiddenInput()
|
||||||
|
|
||||||
|
return form, model_form
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
form, model_form = self.initialize_forms(request)
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'obj': self.queryset.model(),
|
|
||||||
'obj_type': self.queryset.model._meta.verbose_name,
|
'obj_type': self.queryset.model._meta.verbose_name,
|
||||||
'form': form,
|
'form': form,
|
||||||
|
'model_form': model_form,
|
||||||
'return_url': self.get_return_url(request),
|
'return_url': self.get_return_url(request),
|
||||||
})
|
})
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
logger = logging.getLogger('netbox.views.ComponentCreateView')
|
form, model_form = self.initialize_forms(request)
|
||||||
form = self.form(request.POST, initial=request.GET)
|
|
||||||
self.validate_form(request, form)
|
self.validate_form(request, form)
|
||||||
|
|
||||||
if form.is_valid() and not form.errors:
|
if form.is_valid() and not form.errors:
|
||||||
@ -712,6 +726,7 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
|
|||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'obj_type': self.queryset.model._meta.verbose_name,
|
'obj_type': self.queryset.model._meta.verbose_name,
|
||||||
'form': form,
|
'form': form,
|
||||||
|
'model_form': model_form,
|
||||||
'return_url': self.get_return_url(request),
|
'return_url': self.get_return_url(request),
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -734,8 +749,8 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
|
|||||||
data['name'] = name
|
data['name'] = name
|
||||||
data['label'] = label
|
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))
|
||||||
|
|
||||||
component_form = self.model_form(data)
|
component_form = self.model_form(data)
|
||||||
|
|
||||||
|
7
netbox/templates/dcim/component_create.html
Normal file
7
netbox/templates/dcim/component_create.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% extends 'generic/object_edit.html' %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
{% render_form form %}
|
||||||
|
{% render_form model_form %}
|
||||||
|
{% endblock form %}
|
@ -1,81 +1,13 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from dcim.choices import InterfaceModeChoices
|
from utilities.forms import BootstrapMixin, ExpandableNameField
|
||||||
from dcim.forms.common import InterfaceCommonForm
|
|
||||||
from extras.forms import CustomFieldsMixin
|
|
||||||
from extras.models import Tag
|
|
||||||
from ipam.models import VLAN
|
|
||||||
from utilities.forms import (
|
|
||||||
add_blank_choice, BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField,
|
|
||||||
StaticSelect,
|
|
||||||
)
|
|
||||||
from virtualization.models import VMInterface, VirtualMachine
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'VMInterfaceCreateForm',
|
'VMInterfaceCreateForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceCreateForm(BootstrapMixin, CustomFieldsMixin, InterfaceCommonForm):
|
class VMInterfaceCreateForm(BootstrapMixin, forms.Form):
|
||||||
model = VMInterface
|
|
||||||
virtual_machine = DynamicModelChoiceField(
|
|
||||||
queryset=VirtualMachine.objects.all()
|
|
||||||
)
|
|
||||||
name_pattern = ExpandableNameField(
|
name_pattern = ExpandableNameField(
|
||||||
label='Name'
|
label='Name'
|
||||||
)
|
)
|
||||||
enabled = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
initial=True
|
|
||||||
)
|
|
||||||
parent = DynamicModelChoiceField(
|
|
||||||
queryset=VMInterface.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'virtual_machine_id': '$virtual_machine',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
bridge = DynamicModelChoiceField(
|
|
||||||
queryset=VMInterface.objects.all(),
|
|
||||||
required=False,
|
|
||||||
query_params={
|
|
||||||
'virtual_machine_id': '$virtual_machine',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
mac_address = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label='MAC Address'
|
|
||||||
)
|
|
||||||
description = forms.CharField(
|
|
||||||
max_length=200,
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
mode = forms.ChoiceField(
|
|
||||||
choices=add_blank_choice(InterfaceModeChoices),
|
|
||||||
required=False,
|
|
||||||
widget=StaticSelect(),
|
|
||||||
)
|
|
||||||
untagged_vlan = DynamicModelChoiceField(
|
|
||||||
queryset=VLAN.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=VLAN.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
tags = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Tag.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
field_order = (
|
|
||||||
'virtual_machine', 'name_pattern', 'enabled', 'parent', 'bridge', 'mtu', 'mac_address', 'description', 'mode',
|
|
||||||
'untagged_vlan', 'tagged_vlans', 'tags'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
vm_id = self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
|
||||||
|
|
||||||
# Limit VLAN choices by virtual machine
|
|
||||||
self.fields['untagged_vlan'].widget.add_query_param('available_on_virtualmachine', vm_id)
|
|
||||||
self.fields['tagged_vlans'].widget.add_query_param('available_on_virtualmachine', vm_id)
|
|
||||||
|
@ -447,11 +447,11 @@ class VMInterfaceView(generic.ObjectView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# TODO: This should not use ComponentCreateView
|
|
||||||
class VMInterfaceCreateView(generic.ComponentCreateView):
|
class VMInterfaceCreateView(generic.ComponentCreateView):
|
||||||
queryset = VMInterface.objects.all()
|
queryset = VMInterface.objects.all()
|
||||||
form = forms.VMInterfaceCreateForm
|
form = forms.VMInterfaceCreateForm
|
||||||
model_form = forms.VMInterfaceForm
|
model_form = forms.VMInterfaceForm
|
||||||
|
patterned_fields = ('name',)
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceEditView(generic.ObjectEditView):
|
class VMInterfaceEditView(generic.ObjectEditView):
|
||||||
|
Loading…
Reference in New Issue
Block a user