| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- from django import forms
- from dcim.choices import *
- from dcim.constants import *
- from dcim.models import *
- from extras.forms import CustomFieldModelForm, CustomFieldsMixin
- from extras.models import Tag
- from ipam.models import VLAN
- from utilities.forms import (
- add_blank_choice, BootstrapMixin, ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
- ExpandableNameField, StaticSelect,
- )
- from wireless.choices import *
- from .common import InterfaceCommonForm
- __all__ = (
- 'ConsolePortCreateForm',
- 'ConsolePortTemplateCreateForm',
- 'ConsoleServerPortCreateForm',
- 'ConsoleServerPortTemplateCreateForm',
- 'DeviceBayCreateForm',
- 'DeviceBayTemplateCreateForm',
- 'FrontPortCreateForm',
- 'FrontPortTemplateCreateForm',
- 'InterfaceCreateForm',
- 'InterfaceTemplateCreateForm',
- 'InventoryItemCreateForm',
- 'PowerOutletCreateForm',
- 'PowerOutletTemplateCreateForm',
- 'PowerPortCreateForm',
- 'PowerPortTemplateCreateForm',
- 'RearPortCreateForm',
- 'RearPortTemplateCreateForm',
- 'VirtualChassisCreateForm',
- )
- class ComponentForm(forms.Form):
- """
- Subclass this form when facilitating the creation of one or more device component or component templates based on
- a name pattern.
- """
- name_pattern = ExpandableNameField(
- label='Name'
- )
- label_pattern = ExpandableNameField(
- label='Label',
- required=False,
- help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)'
- )
- def clean(self):
- super().clean()
- # Validate that the number of components being created from both the name_pattern and label_pattern are equal
- if self.cleaned_data['label_pattern']:
- name_pattern_count = len(self.cleaned_data['name_pattern'])
- label_pattern_count = len(self.cleaned_data['label_pattern'])
- if name_pattern_count != label_pattern_count:
- raise forms.ValidationError({
- 'label_pattern': f'The provided name pattern will create {name_pattern_count} components, however '
- f'{label_pattern_count} labels will be generated. These counts must match.'
- }, code='label_pattern_mismatch')
- class VirtualChassisCreateForm(BootstrapMixin, CustomFieldModelForm):
- region = DynamicModelChoiceField(
- queryset=Region.objects.all(),
- required=False,
- initial_params={
- 'sites': '$site'
- }
- )
- site_group = DynamicModelChoiceField(
- queryset=SiteGroup.objects.all(),
- required=False,
- initial_params={
- 'sites': '$site'
- }
- )
- site = DynamicModelChoiceField(
- queryset=Site.objects.all(),
- required=False,
- query_params={
- 'region_id': '$region',
- 'group_id': '$site_group',
- }
- )
- rack = DynamicModelChoiceField(
- queryset=Rack.objects.all(),
- required=False,
- null_option='None',
- query_params={
- 'site_id': '$site'
- }
- )
- members = DynamicModelMultipleChoiceField(
- queryset=Device.objects.all(),
- required=False,
- query_params={
- 'site_id': '$site',
- 'rack_id': '$rack',
- }
- )
- initial_position = forms.IntegerField(
- initial=1,
- required=False,
- help_text='Position of the first member device. Increases by one for each additional member.'
- )
- tags = DynamicModelMultipleChoiceField(
- queryset=Tag.objects.all(),
- required=False
- )
- class Meta:
- model = VirtualChassis
- fields = [
- 'name', 'domain', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', 'tags',
- ]
- def save(self, *args, **kwargs):
- instance = super().save(*args, **kwargs)
- # Assign VC members
- if instance.pk:
- initial_position = self.cleaned_data.get('initial_position') or 1
- for i, member in enumerate(self.cleaned_data['members'], start=initial_position):
- member.virtual_chassis = instance
- member.vc_position = i
- member.save()
- return instance
- #
- # Component templates
- #
- class ComponentTemplateCreateForm(BootstrapMixin, 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'
- }
- )
- device_type = DynamicModelChoiceField(
- queryset=DeviceType.objects.all(),
- query_params={
- 'manufacturer_id': '$manufacturer'
- }
- )
- description = forms.CharField(
- required=False
- )
- class ConsolePortTemplateCreateForm(ComponentTemplateCreateForm):
- type = forms.ChoiceField(
- choices=add_blank_choice(ConsolePortTypeChoices),
- widget=StaticSelect()
- )
- field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'description')
- class ConsoleServerPortTemplateCreateForm(ComponentTemplateCreateForm):
- type = forms.ChoiceField(
- choices=add_blank_choice(ConsolePortTypeChoices),
- widget=StaticSelect()
- )
- field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'description')
- class PowerPortTemplateCreateForm(ComponentTemplateCreateForm):
- 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', 'name_pattern', 'label_pattern', 'type', 'maximum_draw', 'allocated_draw',
- 'description',
- )
- class PowerOutletTemplateCreateForm(ComponentTemplateCreateForm):
- type = forms.ChoiceField(
- choices=add_blank_choice(PowerOutletTypeChoices),
- required=False
- )
- power_port = forms.ModelChoiceField(
- queryset=PowerPortTemplate.objects.all(),
- required=False
- )
- feed_leg = forms.ChoiceField(
- choices=add_blank_choice(PowerOutletFeedLegChoices),
- required=False,
- widget=StaticSelect()
- )
- field_order = (
- 'manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'power_port', 'feed_leg',
- 'description',
- )
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- # Limit power_port choices to current DeviceType
- device_type = DeviceType.objects.get(
- pk=self.initial.get('device_type') or self.data.get('device_type')
- )
- self.fields['power_port'].queryset = PowerPortTemplate.objects.filter(
- device_type=device_type
- )
- class InterfaceTemplateCreateForm(ComponentTemplateCreateForm):
- type = forms.ChoiceField(
- choices=InterfaceTypeChoices,
- widget=StaticSelect()
- )
- mgmt_only = forms.BooleanField(
- required=False,
- label='Management only'
- )
- field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'type', 'mgmt_only', 'description')
- class FrontPortTemplateCreateForm(ComponentTemplateCreateForm):
- 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', '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(ComponentTemplateCreateForm):
- 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', 'name_pattern', 'label_pattern', 'type', 'color', 'positions', 'description',
- )
- class DeviceBayTemplateCreateForm(ComponentTemplateCreateForm):
- field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')
- #
- # Device components
- #
- class ComponentCreateForm(BootstrapMixin, 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',
- }
- )
- lag = DynamicModelChoiceField(
- queryset=Interface.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- 'type': 'lag',
- }
- )
- mac_address = forms.CharField(
- required=False,
- label='MAC Address'
- )
- 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
- )
- tagged_vlans = DynamicModelMultipleChoiceField(
- queryset=VLAN.objects.all(),
- required=False
- )
- field_order = (
- 'device', 'name_pattern', 'label_pattern', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
- '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 DeviceBayCreateForm(ComponentCreateForm):
- model = DeviceBay
- field_order = ('device', 'name_pattern', 'label_pattern', 'description', 'tags')
- class InventoryItemCreateForm(ComponentCreateForm):
- model = InventoryItem
- manufacturer = DynamicModelChoiceField(
- queryset=Manufacturer.objects.all(),
- required=False
- )
- parent = DynamicModelChoiceField(
- queryset=InventoryItem.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device'
- }
- )
- 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,
- )
- field_order = (
- 'device', 'parent', 'name_pattern', 'label_pattern', 'manufacturer', 'part_id', 'serial', 'asset_tag',
- 'description', 'tags',
- )
|