from django import forms from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from timezone_field import TimeZoneFormField from dcim.choices import * from dcim.constants import * from dcim.models import * from extras.forms import CustomFieldModelForm from extras.models import Tag from ipam.models import IPAddress, VLAN, VLANGroup from tenancy.forms import TenancyForm from utilities.forms import ( APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SelectWithPK, SmallTextarea, SlugField, StaticSelect, ) from virtualization.models import Cluster, ClusterGroup from wireless.models import WirelessLAN from .common import InterfaceCommonForm __all__ = ( 'CableForm', 'ConsolePortForm', 'ConsolePortTemplateForm', 'ConsoleServerPortForm', 'ConsoleServerPortTemplateForm', 'DeviceBayForm', 'DeviceBayTemplateForm', 'DeviceForm', 'DeviceRoleForm', 'DeviceTypeForm', 'DeviceVCMembershipForm', 'FrontPortForm', 'FrontPortTemplateForm', 'InterfaceForm', 'InterfaceTemplateForm', 'InventoryItemForm', 'LocationForm', 'ManufacturerForm', 'PlatformForm', 'PopulateDeviceBayForm', 'PowerFeedForm', 'PowerOutletForm', 'PowerOutletTemplateForm', 'PowerPanelForm', 'PowerPortForm', 'PowerPortTemplateForm', 'RackForm', 'RackReservationForm', 'RackRoleForm', 'RearPortForm', 'RearPortTemplateForm', 'RegionForm', 'SiteForm', 'SiteGroupForm', 'VCMemberSelectForm', 'VirtualChassisForm', ) INTERFACE_MODE_HELP_TEXT = """ Access: One untagged VLAN
Tagged: One untagged VLAN and/or one or more tagged VLANs
Tagged (All): Implies all VLANs are available (w/optional untagged VLAN) """ class RegionForm(BootstrapMixin, CustomFieldModelForm): parent = DynamicModelChoiceField( queryset=Region.objects.all(), required=False ) slug = SlugField() class Meta: model = Region fields = ( 'parent', 'name', 'slug', 'description', ) class SiteGroupForm(BootstrapMixin, CustomFieldModelForm): parent = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False ) slug = SlugField() class Meta: model = SiteGroup fields = ( 'parent', 'name', 'slug', 'description', ) class SiteForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False ) group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False ) slug = SlugField() time_zone = TimeZoneFormField( choices=add_blank_choice(TimeZoneFormField().choices), required=False, widget=StaticSelect() ) comments = CommentField() tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = Site fields = [ 'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email', 'comments', 'tags', ] fieldsets = ( ('Site', ( 'name', 'slug', 'status', 'region', 'group', 'facility', 'asn', 'time_zone', 'description', 'tags', )), ('Tenancy', ('tenant_group', 'tenant')), ('Contact Info', ( 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email', )), ) widgets = { 'physical_address': SmallTextarea( attrs={ 'rows': 3, } ), 'shipping_address': SmallTextarea( attrs={ 'rows': 3, } ), 'status': StaticSelect(), 'time_zone': StaticSelect(), } help_texts = { 'name': "Full name of the site", 'facility': "Data center provider and facility (e.g. Equinix NY7)", 'asn': "BGP autonomous system number", 'time_zone': "Local time zone", 'description': "Short description (will appear in sites list)", 'physical_address': "Physical location of the building (e.g. for GPS)", 'shipping_address': "If different from the physical address", 'latitude': "Latitude in decimal format (xx.yyyyyy)", 'longitude': "Longitude in decimal format (xx.yyyyyy)" } class LocationForm(BootstrapMixin, TenancyForm, 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(), query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) parent = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) slug = SlugField() class Meta: model = Location fields = ( 'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tenant_group', 'tenant', ) fieldsets = ( ('Location', ( 'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', )), ('Tenancy', ('tenant_group', 'tenant')), ) class RackRoleForm(BootstrapMixin, CustomFieldModelForm): slug = SlugField() class Meta: model = RackRole fields = [ 'name', 'slug', 'color', 'description', ] class RackForm(BootstrapMixin, TenancyForm, 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(), query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) role = DynamicModelChoiceField( queryset=RackRole.objects.all(), required=False ) comments = CommentField() tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = Rack fields = [ 'region', 'site_group', 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments', 'tags', ] help_texts = { 'site': "The site at which the rack exists", 'name': "Organizational rack name", 'facility_id': "The unique rack ID assigned by the facility", 'u_height': "Height in rack units", } widgets = { 'status': StaticSelect(), 'type': StaticSelect(), 'width': StaticSelect(), 'outer_unit': StaticSelect(), } class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False, initial_params={ 'sites': '$site' }, fetch_trigger='open' ) site_group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' }, fetch_trigger='open' ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, query_params={ 'region_id': '$region', 'group_id': '$site_group', }, fetch_trigger='open' ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' }, fetch_trigger='open' ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), query_params={ 'site_id': '$site', 'location_id': '$location', }, fetch_trigger='open' ) units = NumericArrayField( base_field=forms.IntegerField(), help_text="Comma-separated list of numeric unit IDs. A range may be specified using a hyphen." ) user = forms.ModelChoiceField( queryset=User.objects.order_by( 'username' ), widget=StaticSelect() ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False, fetch_trigger='open' ) class Meta: model = RackReservation fields = [ 'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant', 'description', 'tags', ] fieldsets = ( ('Reservation', ('region', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')), ('Tenancy', ('tenant_group', 'tenant')), ) class ManufacturerForm(BootstrapMixin, CustomFieldModelForm): slug = SlugField() class Meta: model = Manufacturer fields = [ 'name', 'slug', 'description', ] class DeviceTypeForm(BootstrapMixin, CustomFieldModelForm): manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all() ) slug = SlugField( slug_source='model' ) comments = CommentField() tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = DeviceType fields = [ 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'front_image', 'rear_image', 'comments', 'tags', ] fieldsets = ( ('Device Type', ( 'manufacturer', 'model', 'slug', 'part_number', 'tags', )), ('Chassis', ( 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', )), ('Images', ('front_image', 'rear_image')), ) widgets = { 'subdevice_role': StaticSelect(), 'front_image': ClearableFileInput(attrs={ 'accept': DEVICETYPE_IMAGE_FORMATS }), 'rear_image': ClearableFileInput(attrs={ 'accept': DEVICETYPE_IMAGE_FORMATS }) } class DeviceRoleForm(BootstrapMixin, CustomFieldModelForm): slug = SlugField() class Meta: model = DeviceRole fields = [ 'name', 'slug', 'color', 'vm_role', 'description', ] class PlatformForm(BootstrapMixin, CustomFieldModelForm): manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False ) slug = SlugField( max_length=64 ) class Meta: model = Platform fields = [ 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description', ] widgets = { 'napalm_args': SmallTextarea(), } class DeviceForm(BootstrapMixin, TenancyForm, 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(), query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' }, initial_params={ 'racks': '$rack' } ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), required=False, query_params={ 'site_id': '$site', 'location_id': '$location', } ) position = forms.IntegerField( required=False, help_text="The lowest-numbered unit occupied by the device", widget=APISelect( api_url='/api/dcim/racks/{{rack}}/elevation/', attrs={ 'disabled-indicator': 'device', 'data-dynamic-params': '[{"fieldName":"face","queryParam":"face"}]' } ) ) 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' } ) device_role = DynamicModelChoiceField( queryset=DeviceRole.objects.all() ) platform = DynamicModelChoiceField( queryset=Platform.objects.all(), required=False, query_params={ 'manufacturer_id': ['$manufacturer', 'null'] } ) cluster_group = DynamicModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, null_option='None', initial_params={ 'clusters': '$cluster' } ) cluster = DynamicModelChoiceField( queryset=Cluster.objects.all(), required=False, query_params={ 'group_id': '$cluster_group' } ) comments = CommentField() local_context_data = JSONField( required=False, label='' ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = Device fields = [ 'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack', 'location', 'position', 'face', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', 'cluster_group', 'cluster', 'tenant_group', 'tenant', 'comments', 'tags', 'local_context_data' ] help_texts = { 'device_role': "The function this device serves", 'serial': "Chassis serial number", 'local_context_data': "Local config context data overwrites all source contexts in the final rendered " "config context", } widgets = { 'face': StaticSelect(), 'status': StaticSelect(), 'airflow': StaticSelect(), 'primary_ip4': StaticSelect(), 'primary_ip6': StaticSelect(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance.pk: # Compile list of choices for primary IPv4 and IPv6 addresses for family in [4, 6]: ip_choices = [(None, '---------')] # Gather PKs of all interfaces belonging to this Device or a peer VirtualChassis member interface_ids = self.instance.vc_interfaces(if_master=False).values_list('pk', flat=True) # Collect interface IPs interface_ips = IPAddress.objects.filter( address__family=family, assigned_object_type=ContentType.objects.get_for_model(Interface), assigned_object_id__in=interface_ids ).prefetch_related('assigned_object') if interface_ips: ip_list = [(ip.id, f'{ip.address} ({ip.assigned_object})') for ip in interface_ips] ip_choices.append(('Interface IPs', ip_list)) # Collect NAT IPs nat_ips = IPAddress.objects.prefetch_related('nat_inside').filter( address__family=family, nat_inside__assigned_object_type=ContentType.objects.get_for_model(Interface), nat_inside__assigned_object_id__in=interface_ids ).prefetch_related('assigned_object') if nat_ips: ip_list = [(ip.id, f'{ip.address} (NAT)') for ip in nat_ips] ip_choices.append(('NAT IPs', ip_list)) self.fields['primary_ip{}'.format(family)].choices = ip_choices # If editing an existing device, exclude it from the list of occupied rack units. This ensures that a device # can be flipped from one face to another. self.fields['position'].widget.add_query_param('exclude', self.instance.pk) # Limit platform by manufacturer self.fields['platform'].queryset = Platform.objects.filter( Q(manufacturer__isnull=True) | Q(manufacturer=self.instance.device_type.manufacturer) ) # Disable rack assignment if this is a child device installed in a parent device if self.instance.device_type.is_child_device and hasattr(self.instance, 'parent_bay'): self.fields['site'].disabled = True self.fields['rack'].disabled = True self.initial['site'] = self.instance.parent_bay.device.site_id self.initial['rack'] = self.instance.parent_bay.device.rack_id else: # An object that doesn't exist yet can't have any IPs assigned to it self.fields['primary_ip4'].choices = [] self.fields['primary_ip4'].widget.attrs['readonly'] = True self.fields['primary_ip6'].choices = [] self.fields['primary_ip6'].widget.attrs['readonly'] = True # Rack position position = self.data.get('position') or self.initial.get('position') if position: self.fields['position'].widget.choices = [(position, f'U{position}')] class CableForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = Cable fields = [ 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags', ] widgets = { 'status': StaticSelect, 'type': StaticSelect, 'length_unit': StaticSelect, } error_messages = { 'length': { 'max_value': 'Maximum length is 32767 (any unit)' } } class PowerPanelForm(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(), query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = PowerPanel fields = [ 'region', 'site_group', 'site', 'location', 'name', 'tags', ] fieldsets = ( ('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'tags')), ) class PowerFeedForm(BootstrapMixin, CustomFieldModelForm): region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False, initial_params={ 'sites__powerpanel': '$power_panel' } ) site_group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, initial_params={ 'powerpanel': '$power_panel' }, query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) power_panel = DynamicModelChoiceField( queryset=PowerPanel.objects.all(), query_params={ 'site_id': '$site' } ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), required=False, query_params={ 'site_id': '$site' } ) comments = CommentField() tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = PowerFeed fields = [ 'region', 'site_group', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization', 'comments', 'tags', ] fieldsets = ( ('Power Panel', ('region', 'site', 'power_panel')), ('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')), ('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')), ) widgets = { 'status': StaticSelect(), 'type': StaticSelect(), 'supply': StaticSelect(), 'phase': StaticSelect(), } # # Virtual chassis # class VirtualChassisForm(BootstrapMixin, CustomFieldModelForm): master = forms.ModelChoiceField( queryset=Device.objects.all(), required=False, ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = VirtualChassis fields = [ 'name', 'domain', 'master', 'tags', ] widgets = { 'master': SelectWithPK(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['master'].queryset = Device.objects.filter(virtual_chassis=self.instance) class DeviceVCMembershipForm(forms.ModelForm): class Meta: model = Device fields = [ 'vc_position', 'vc_priority', ] labels = { 'vc_position': 'Position', 'vc_priority': 'Priority', } def __init__(self, validate_vc_position=False, *args, **kwargs): super().__init__(*args, **kwargs) # Require VC position (only required when the Device is a VirtualChassis member) self.fields['vc_position'].required = True # Add bootstrap classes to form elements. self.fields['vc_position'].widget.attrs = {'class': 'form-control'} self.fields['vc_priority'].widget.attrs = {'class': 'form-control'} # Validation of vc_position is optional. This is only required when adding a new member to an existing # VirtualChassis. Otherwise, vc_position validation is handled by BaseVCMemberFormSet. self.validate_vc_position = validate_vc_position def clean_vc_position(self): vc_position = self.cleaned_data['vc_position'] if self.validate_vc_position: conflicting_members = Device.objects.filter( virtual_chassis=self.instance.virtual_chassis, vc_position=vc_position ) if conflicting_members.exists(): raise forms.ValidationError( 'A virtual chassis member already exists in position {}.'.format(vc_position) ) return vc_position class VCMemberSelectForm(BootstrapMixin, forms.Form): 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' } ) device = DynamicModelChoiceField( queryset=Device.objects.all(), query_params={ 'site_id': '$site', 'rack_id': '$rack', 'virtual_chassis_id': 'null', } ) def clean_device(self): device = self.cleaned_data['device'] if device.virtual_chassis is not None: raise forms.ValidationError( f"Device {device} is already assigned to a virtual chassis." ) return device # # Device component templates # class ConsolePortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConsolePortTemplate fields = [ 'device_type', 'name', 'label', 'type', 'description', ] widgets = { 'device_type': forms.HiddenInput(), } class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConsoleServerPortTemplate fields = [ 'device_type', 'name', 'label', 'type', 'description', ] widgets = { 'device_type': forms.HiddenInput(), } class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = PowerPortTemplate fields = [ 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', ] widgets = { 'device_type': forms.HiddenInput(), } class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = PowerOutletTemplate fields = [ 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', ] widgets = { 'device_type': forms.HiddenInput(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit power_port choices to current DeviceType if hasattr(self.instance, 'device_type'): self.fields['power_port'].queryset = PowerPortTemplate.objects.filter( device_type=self.instance.device_type ) class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = InterfaceTemplate fields = [ 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', ] widgets = { 'device_type': forms.HiddenInput(), 'type': StaticSelect(), } class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = FrontPortTemplate fields = [ 'device_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', ] widgets = { 'device_type': forms.HiddenInput(), 'rear_port': StaticSelect(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit rear_port choices to current DeviceType if hasattr(self.instance, 'device_type'): self.fields['rear_port'].queryset = RearPortTemplate.objects.filter( device_type=self.instance.device_type ) class RearPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = RearPortTemplate fields = [ 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description', ] widgets = { 'device_type': forms.HiddenInput(), 'type': StaticSelect(), } class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = DeviceBayTemplate fields = [ 'device_type', 'name', 'label', 'description', ] widgets = { 'device_type': forms.HiddenInput(), } # # Device components # class ConsolePortForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = ConsolePort fields = [ 'device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), } class ConsoleServerPortForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = ConsoleServerPort fields = [ 'device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), } class PowerPortForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = PowerPort fields = [ 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), } class PowerOutletForm(BootstrapMixin, CustomFieldModelForm): power_port = forms.ModelChoiceField( queryset=PowerPort.objects.all(), required=False ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = PowerOutlet fields = [ 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit power_port choices to the local device if hasattr(self.instance, 'device'): self.fields['power_port'].queryset = PowerPort.objects.filter( device=self.instance.device ) class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm): parent = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, label='Parent interface' ) lag = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, label='LAG interface', query_params={ 'type': 'lag', } ) wireless_lans = DynamicModelMultipleChoiceField( queryset=WirelessLAN.objects.all(), required=False, label='Wireless LANs' ) vlan_group = DynamicModelChoiceField( queryset=VLANGroup.objects.all(), required=False, label='VLAN group' ) untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, label='Untagged VLAN', query_params={ 'group_id': '$vlan_group', } ) tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, label='Tagged VLANs', query_params={ 'group_id': '$vlan_group', } ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = Interface fields = [ 'device', 'name', 'label', 'type', 'enabled', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_width', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'tags', ] widgets = { 'device': forms.HiddenInput(), 'type': StaticSelect(), 'mode': StaticSelect(), 'rf_role': StaticSelect(), 'rf_channel': StaticSelect(), 'rf_channel_width': StaticSelect(), } labels = { 'mode': '802.1Q Mode', } help_texts = { 'mode': INTERFACE_MODE_HELP_TEXT, } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) device = Device.objects.get(pk=self.data['device']) if self.is_bound else self.instance.device # Restrict parent/LAG interface assignment by device/VC self.fields['parent'].widget.add_query_param('device_id', device.pk) if device.virtual_chassis and device.virtual_chassis.master: # Get available LAG interfaces by VirtualChassis master self.fields['lag'].widget.add_query_param('device_id', device.virtual_chassis.master.pk) else: self.fields['lag'].widget.add_query_param('device_id', device.pk) # Limit VLAN choices by device self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device.pk) self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device.pk) class FrontPortForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = FrontPort fields = [ 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), 'type': StaticSelect(), 'rear_port': StaticSelect(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit RearPort choices to the local device if hasattr(self.instance, 'device'): self.fields['rear_port'].queryset = self.fields['rear_port'].queryset.filter( device=self.instance.device ) class RearPortForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = RearPort fields = [ 'device', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), 'type': StaticSelect(), } class DeviceBayForm(BootstrapMixin, CustomFieldModelForm): tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = DeviceBay fields = [ 'device', 'name', 'label', 'description', 'tags', ] widgets = { 'device': forms.HiddenInput(), } class PopulateDeviceBayForm(BootstrapMixin, forms.Form): installed_device = forms.ModelChoiceField( queryset=Device.objects.all(), label='Child Device', help_text="Child devices must first be created and assigned to the site/rack of the parent device.", widget=StaticSelect(), ) def __init__(self, device_bay, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['installed_device'].queryset = Device.objects.filter( site=device_bay.device.site, rack=device_bay.device.rack, parent_bay__isnull=True, device_type__u_height=0, device_type__subdevice_role=SubdeviceRoleChoices.ROLE_CHILD ).exclude(pk=device_bay.device.pk) class InventoryItemForm(BootstrapMixin, CustomFieldModelForm): device = DynamicModelChoiceField( queryset=Device.objects.all() ) parent = DynamicModelChoiceField( queryset=InventoryItem.objects.all(), required=False, query_params={ 'device_id': '$device' } ) manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) class Meta: model = InventoryItem fields = [ 'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'tags', ]