from django import forms from django.conf import settings from django.utils.translation import gettext_lazy as _ from timezone_field import TimeZoneFormField from dcim.choices import * from dcim.constants import * from dcim.models import * from extras.models import ConfigTemplate from ipam.choices import VLANQinQRoleChoices from ipam.models import ASN, VLAN, VLANGroup, VRF from netbox.choices import * from netbox.forms import ( NestedGroupModelBulkEditForm, NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm, ) from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin from tenancy.models import Tenant from users.models import User from utilities.forms import BulkEditForm, add_blank_choice, form_from_model from utilities.forms.fields import ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions from virtualization.models import Cluster from wireless.choices import WirelessRoleChoices from wireless.models import WirelessLAN, WirelessLANGroup __all__ = ( 'CableBulkEditForm', 'ConsolePortBulkEditForm', 'ConsolePortTemplateBulkEditForm', 'ConsoleServerPortBulkEditForm', 'ConsoleServerPortTemplateBulkEditForm', 'DeviceBayBulkEditForm', 'DeviceBayTemplateBulkEditForm', 'DeviceBulkEditForm', 'DeviceRoleBulkEditForm', 'DeviceTypeBulkEditForm', 'FrontPortBulkEditForm', 'FrontPortTemplateBulkEditForm', 'InterfaceBulkEditForm', 'InterfaceTemplateBulkEditForm', 'InventoryItemBulkEditForm', 'InventoryItemRoleBulkEditForm', 'InventoryItemTemplateBulkEditForm', 'LocationBulkEditForm', 'MACAddressBulkEditForm', 'ManufacturerBulkEditForm', 'ModuleBulkEditForm', 'ModuleBayBulkEditForm', 'ModuleBayTemplateBulkEditForm', 'ModuleTypeBulkEditForm', 'ModuleTypeProfileBulkEditForm', 'PlatformBulkEditForm', 'PowerFeedBulkEditForm', 'PowerOutletBulkEditForm', 'PowerOutletTemplateBulkEditForm', 'PowerPanelBulkEditForm', 'PowerPortBulkEditForm', 'PowerPortTemplateBulkEditForm', 'RackBulkEditForm', 'RackReservationBulkEditForm', 'RackRoleBulkEditForm', 'RackTypeBulkEditForm', 'RearPortBulkEditForm', 'RearPortTemplateBulkEditForm', 'RegionBulkEditForm', 'SiteBulkEditForm', 'SiteGroupBulkEditForm', 'VirtualChassisBulkEditForm', 'VirtualDeviceContextBulkEditForm' ) class RegionBulkEditForm(NestedGroupModelBulkEditForm): parent = DynamicModelChoiceField( label=_('Parent'), queryset=Region.objects.all(), required=False ) model = Region fieldsets = ( FieldSet('parent', 'description'), ) nullable_fields = ('parent', 'description', 'comments') class SiteGroupBulkEditForm(NestedGroupModelBulkEditForm): parent = DynamicModelChoiceField( label=_('Parent'), queryset=SiteGroup.objects.all(), required=False ) model = SiteGroup fieldsets = ( FieldSet('parent', 'description'), ) nullable_fields = ('parent', 'description', 'comments') class SiteBulkEditForm(PrimaryModelBulkEditForm): status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(SiteStatusChoices), required=False, initial='' ) region = DynamicModelChoiceField( label=_('Region'), queryset=Region.objects.all(), required=False ) group = DynamicModelChoiceField( label=_('Group'), queryset=SiteGroup.objects.all(), required=False ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) facility = forms.CharField( label=_('Facility'), max_length=50, required=False ) asns = DynamicModelMultipleChoiceField( queryset=ASN.objects.all(), label=_('ASNs'), required=False ) contact_name = forms.CharField( label=_('Contact name'), max_length=50, required=False ) contact_phone = forms.CharField( label=_('Contact phone'), max_length=20, required=False ) contact_email = forms.EmailField( required=False, label=_('Contact E-mail') ) time_zone = TimeZoneFormField( label=_('Time zone'), choices=add_blank_choice(TimeZoneFormField().choices), required=False ) model = Site fieldsets = ( FieldSet('status', 'region', 'group', 'tenant', 'facility', 'asns', 'time_zone', 'description'), ) nullable_fields = ( 'region', 'group', 'tenant', 'facility', 'asns', 'time_zone', 'description', 'comments', ) class LocationBulkEditForm(NestedGroupModelBulkEditForm): site = DynamicModelChoiceField( label=_('Site'), queryset=Site.objects.all(), required=False ) parent = DynamicModelChoiceField( label=_('Parent'), queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(LocationStatusChoices), required=False, initial='' ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) facility = forms.CharField( label=_('Facility'), max_length=50, required=False ) model = Location fieldsets = ( FieldSet('site', 'parent', 'status', 'tenant', 'facility', 'description'), ) nullable_fields = ('parent', 'tenant', 'facility', 'description', 'comments') class RackRoleBulkEditForm(OrganizationalModelBulkEditForm): color = ColorField( label=_('Color'), required=False ) model = RackRole fieldsets = ( FieldSet('color', 'description'), ) nullable_fields = ('color', 'description', 'comments') class RackTypeBulkEditForm(PrimaryModelBulkEditForm): manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) form_factor = forms.ChoiceField( label=_('Form factor'), choices=add_blank_choice(RackFormFactorChoices), required=False ) width = forms.ChoiceField( label=_('Width'), choices=add_blank_choice(RackWidthChoices), required=False ) u_height = forms.IntegerField( required=False, label=_('Height (U)') ) starting_unit = forms.IntegerField( required=False, min_value=1 ) desc_units = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label=_('Descending units') ) outer_width = forms.IntegerField( label=_('Outer width'), required=False, min_value=1 ) outer_height = forms.IntegerField( label=_('Outer height'), required=False, min_value=1 ) outer_depth = forms.IntegerField( label=_('Outer depth'), required=False, min_value=1 ) outer_unit = forms.ChoiceField( label=_('Outer unit'), choices=add_blank_choice(RackDimensionUnitChoices), required=False ) mounting_depth = forms.IntegerField( label=_('Mounting depth'), required=False, min_value=1 ) weight = forms.DecimalField( label=_('Weight'), min_value=0, required=False ) max_weight = forms.IntegerField( label=_('Max weight'), min_value=0, required=False ) weight_unit = forms.ChoiceField( label=_('Weight unit'), choices=add_blank_choice(WeightUnitChoices), required=False, initial='' ) model = RackType fieldsets = ( FieldSet('manufacturer', 'description', 'form_factor', 'width', 'u_height', name=_('Rack Type')), FieldSet( InlineFields('outer_width', 'outer_height', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), 'mounting_depth', name=_('Dimensions') ), FieldSet('starting_unit', 'desc_units', name=_('Numbering')), ) nullable_fields = ( 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', ) class RackBulkEditForm(PrimaryModelBulkEditForm): region = DynamicModelChoiceField( label=_('Region'), queryset=Region.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site_group = DynamicModelChoiceField( label=_('Site group'), queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site = DynamicModelChoiceField( label=_('Site'), queryset=Site.objects.all(), required=False, query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( label=_('Location'), queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(RackStatusChoices), required=False, initial='' ) role = DynamicModelChoiceField( label=_('Role'), queryset=RackRole.objects.all(), required=False ) rack_type = DynamicModelChoiceField( label=_('Rack type'), queryset=RackType.objects.all(), required=False, ) serial = forms.CharField( max_length=50, required=False, label=_('Serial Number') ) asset_tag = forms.CharField( label=_('Asset tag'), max_length=50, required=False ) form_factor = forms.ChoiceField( label=_('Form factor'), choices=add_blank_choice(RackFormFactorChoices), required=False ) width = forms.ChoiceField( label=_('Width'), choices=add_blank_choice(RackWidthChoices), required=False ) u_height = forms.IntegerField( required=False, label=_('Height (U)') ) desc_units = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label=_('Descending units') ) outer_width = forms.IntegerField( label=_('Outer width'), required=False, min_value=1 ) outer_height = forms.IntegerField( label=_('Outer height'), required=False, min_value=1 ) outer_depth = forms.IntegerField( label=_('Outer depth'), required=False, min_value=1 ) outer_unit = forms.ChoiceField( label=_('Outer unit'), choices=add_blank_choice(RackDimensionUnitChoices), required=False ) mounting_depth = forms.IntegerField( label=_('Mounting depth'), required=False, min_value=1 ) airflow = forms.ChoiceField( label=_('Airflow'), choices=add_blank_choice(RackAirflowChoices), required=False ) weight = forms.DecimalField( label=_('Weight'), min_value=0, required=False ) max_weight = forms.IntegerField( label=_('Max weight'), min_value=0, required=False ) weight_unit = forms.ChoiceField( label=_('Weight unit'), choices=add_blank_choice(WeightUnitChoices), required=False, initial='' ) model = Rack fieldsets = ( FieldSet('status', 'role', 'tenant', 'serial', 'asset_tag', 'rack_type', 'description', name=_('Rack')), FieldSet('region', 'site_group', 'site', 'location', name=_('Location')), FieldSet('outer_width', 'outer_height', 'outer_depth', 'outer_unit', name=_('Outer Dimensions')), FieldSet('form_factor', 'width', 'u_height', 'desc_units', 'airflow', 'mounting_depth', name=_('Hardware')), FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')), ) nullable_fields = ( 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', ) class RackReservationBulkEditForm(PrimaryModelBulkEditForm): status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(RackReservationStatusChoices), required=False, initial='' ) user = forms.ModelChoiceField( label=_('User'), queryset=User.objects.order_by('username'), required=False ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) model = RackReservation fieldsets = ( FieldSet('status', 'user', 'tenant', 'description'), ) nullable_fields = ('comments',) class ManufacturerBulkEditForm(OrganizationalModelBulkEditForm): model = Manufacturer fieldsets = ( FieldSet('description'), ) nullable_fields = ('description', 'comments') class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm): manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) default_platform = DynamicModelChoiceField( label=_('Default platform'), queryset=Platform.objects.all(), required=False ) part_number = forms.CharField( label=_('Part number'), required=False ) u_height = forms.IntegerField( label=_('U height'), min_value=0, required=False ) is_full_depth = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect(), label=_('Is full depth') ) exclude_from_utilization = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect(), label=_('Exclude from utilization') ) airflow = forms.ChoiceField( label=_('Airflow'), choices=add_blank_choice(DeviceAirflowChoices), required=False ) weight = forms.DecimalField( label=_('Weight'), min_value=0, required=False ) weight_unit = forms.ChoiceField( label=_('Weight unit'), choices=add_blank_choice(WeightUnitChoices), required=False, initial='' ) model = DeviceType fieldsets = ( FieldSet( 'manufacturer', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth', 'airflow', 'description', name=_('Device Type') ), FieldSet('weight', 'weight_unit', name=_('Weight')), ) nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments') class ModuleTypeProfileBulkEditForm(PrimaryModelBulkEditForm): schema = JSONField( label=_('Schema'), required=False ) model = ModuleTypeProfile fieldsets = ( FieldSet('name', 'description', 'schema', name=_('Profile')), ) nullable_fields = ('description', 'comments') class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm): profile = DynamicModelChoiceField( label=_('Profile'), queryset=ModuleTypeProfile.objects.all(), required=False ) manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) part_number = forms.CharField( label=_('Part number'), required=False ) airflow = forms.ChoiceField( label=_('Airflow'), choices=add_blank_choice(ModuleAirflowChoices), required=False ) weight = forms.DecimalField( label=_('Weight'), min_value=0, required=False ) weight_unit = forms.ChoiceField( label=_('Weight unit'), choices=add_blank_choice(WeightUnitChoices), required=False, initial='' ) model = ModuleType fieldsets = ( FieldSet('profile', 'manufacturer', 'part_number', 'description', name=_('Module Type')), FieldSet( 'airflow', InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), name=_('Chassis') ), ) nullable_fields = ('part_number', 'weight', 'weight_unit', 'profile', 'description', 'comments') class DeviceRoleBulkEditForm(NestedGroupModelBulkEditForm): parent = DynamicModelChoiceField( label=_('Parent'), queryset=DeviceRole.objects.all(), required=False, ) color = ColorField( label=_('Color'), required=False ) vm_role = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label=_('VM role') ) config_template = DynamicModelChoiceField( label=_('Config template'), queryset=ConfigTemplate.objects.all(), required=False ) model = DeviceRole fieldsets = ( FieldSet('parent', 'color', 'vm_role', 'config_template', 'description'), ) nullable_fields = ('parent', 'color', 'config_template', 'description', 'comments') class PlatformBulkEditForm(NestedGroupModelBulkEditForm): parent = DynamicModelChoiceField( label=_('Parent'), queryset=Platform.objects.all(), required=False, ) manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) config_template = DynamicModelChoiceField( label=_('Config template'), queryset=ConfigTemplate.objects.all(), required=False ) model = Platform fieldsets = ( FieldSet('parent', 'manufacturer', 'config_template', 'description'), ) nullable_fields = ('parent', 'manufacturer', 'config_template', 'description', 'comments') class DeviceBulkEditForm(PrimaryModelBulkEditForm): manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) device_type = DynamicModelChoiceField( label=_('Device type'), queryset=DeviceType.objects.all(), required=False, context={ 'parent': 'manufacturer', }, query_params={ 'manufacturer_id': '$manufacturer' } ) role = DynamicModelChoiceField( label=_('Device role'), queryset=DeviceRole.objects.all(), required=False ) site = DynamicModelChoiceField( label=_('Site'), queryset=Site.objects.all(), required=False ) location = DynamicModelChoiceField( label=_('Location'), queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) platform = DynamicModelChoiceField( label=_('Platform'), queryset=Platform.objects.all(), required=False ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(DeviceStatusChoices), required=False ) airflow = forms.ChoiceField( label=_('Airflow'), choices=add_blank_choice(DeviceAirflowChoices), required=False ) serial = forms.CharField( max_length=50, required=False, label=_('Serial Number') ) config_template = DynamicModelChoiceField( label=_('Config template'), queryset=ConfigTemplate.objects.all(), required=False ) cluster = DynamicModelChoiceField( label=_('Cluster'), queryset=Cluster.objects.all(), required=False, query_params={ 'site_id': ['$site', 'null'] }, ) model = Device fieldsets = ( FieldSet('role', 'status', 'tenant', 'platform', 'description', name=_('Device')), FieldSet('site', 'location', name=_('Location')), FieldSet('manufacturer', 'device_type', 'airflow', 'serial', name=_('Hardware')), FieldSet('config_template', name=_('Configuration')), FieldSet('cluster', name=_('Virtualization')), ) nullable_fields = ( 'location', 'tenant', 'platform', 'serial', 'airflow', 'description', 'cluster', 'comments', ) class ModuleBulkEditForm(PrimaryModelBulkEditForm): manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) module_type = DynamicModelChoiceField( label=_('Module type'), queryset=ModuleType.objects.all(), required=False, query_params={ 'manufacturer_id': '$manufacturer' }, context={ 'parent': 'manufacturer', } ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(ModuleStatusChoices), required=False, initial='' ) serial = forms.CharField( max_length=50, required=False, label=_('Serial Number') ) model = Module fieldsets = ( FieldSet('manufacturer', 'module_type', 'status', 'serial', 'description'), ) nullable_fields = ('serial', 'description', 'comments') class CableBulkEditForm(PrimaryModelBulkEditForm): type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(CableTypeChoices), required=False, initial='' ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(LinkStatusChoices), required=False, initial='' ) profile = forms.ChoiceField( label=_('Profile'), choices=add_blank_choice(CableProfileChoices), required=False, initial='' ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) label = forms.CharField( label=_('Label'), max_length=100, required=False ) color = ColorField( label=_('Color'), required=False ) length = forms.DecimalField( label=_('Length'), min_value=0, required=False ) length_unit = forms.ChoiceField( label=_('Length unit'), choices=add_blank_choice(CableLengthUnitChoices), required=False, initial='' ) model = Cable fieldsets = ( FieldSet('type', 'status', 'profile', 'tenant', 'label', 'description'), FieldSet('color', 'length', 'length_unit', name=_('Attributes')), ) nullable_fields = ( 'type', 'status', 'profile', 'tenant', 'label', 'color', 'length', 'description', 'comments', ) class VirtualChassisBulkEditForm(PrimaryModelBulkEditForm): domain = forms.CharField( label=_('Domain'), max_length=30, required=False ) model = VirtualChassis fieldsets = ( FieldSet('domain', 'description'), ) nullable_fields = ('domain', 'description', 'comments') class PowerPanelBulkEditForm(PrimaryModelBulkEditForm): region = DynamicModelChoiceField( label=_('Region'), queryset=Region.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site_group = DynamicModelChoiceField( label=_('Site group'), queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site = DynamicModelChoiceField( label=_('Site'), queryset=Site.objects.all(), required=False, query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( label=_('Location'), queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) model = PowerPanel fieldsets = ( FieldSet('region', 'site_group', 'site', 'location', 'description'), ) nullable_fields = ('location', 'description', 'comments') class PowerFeedBulkEditForm(PrimaryModelBulkEditForm): power_panel = DynamicModelChoiceField( label=_('Power panel'), queryset=PowerPanel.objects.all(), required=False ) rack = DynamicModelChoiceField( label=_('Rack'), queryset=Rack.objects.all(), required=False, ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(PowerFeedStatusChoices), required=False, initial='' ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(PowerFeedTypeChoices), required=False, initial='' ) supply = forms.ChoiceField( label=_('Supply'), choices=add_blank_choice(PowerFeedSupplyChoices), required=False, initial='' ) phase = forms.ChoiceField( label=_('Phase'), choices=add_blank_choice(PowerFeedPhaseChoices), required=False, initial='' ) voltage = forms.IntegerField( label=_('Voltage'), required=False ) amperage = forms.IntegerField( label=_('Amperage'), required=False ) max_utilization = forms.IntegerField( label=_('Max utilization'), required=False ) mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) model = PowerFeed fieldsets = ( FieldSet('power_panel', 'rack', 'status', 'type', 'mark_connected', 'description', 'tenant'), FieldSet('supply', 'phase', 'voltage', 'amperage', 'max_utilization', name=_('Power')) ) nullable_fields = ('location', 'tenant', 'description', 'comments') # # Device component templates # class ComponentTemplateBulkEditForm(ChangelogMessageMixin, BulkEditForm): pass class ConsolePortTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=ConsolePortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(ConsolePortTypeChoices), required=False ) nullable_fields = ('label', 'type', 'description') class ConsoleServerPortTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=ConsoleServerPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(ConsolePortTypeChoices), required=False ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('label', 'type', 'description') class PowerPortTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=PowerPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(PowerPortTypeChoices), required=False ) maximum_draw = forms.IntegerField( label=_('Maximum draw'), min_value=1, required=False, help_text=_("Maximum power draw (watts)") ) allocated_draw = forms.IntegerField( label=_('Allocated draw'), min_value=1, required=False, help_text=_("Allocated power draw (watts)") ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('label', 'type', 'maximum_draw', 'allocated_draw', 'description') class PowerOutletTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=PowerOutletTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) device_type = forms.ModelChoiceField( label=_('Device type'), queryset=DeviceType.objects.all(), required=False, disabled=True, widget=forms.HiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(PowerOutletTypeChoices), required=False ) color = ColorField( label=_('Color'), required=False ) power_port = forms.ModelChoiceField( label=_('Power port'), queryset=PowerPortTemplate.objects.all(), required=False ) feed_leg = forms.ChoiceField( label=_('Feed leg'), choices=add_blank_choice(PowerOutletFeedLegChoices), required=False ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('label', 'type', 'power_port', 'feed_leg', 'description') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit power_port queryset to PowerPortTemplates which belong to the parent DeviceType if 'device_type' in self.initial: device_type = DeviceType.objects.filter(pk=self.initial['device_type']).first() self.fields['power_port'].queryset = PowerPortTemplate.objects.filter(device_type=device_type) else: self.fields['power_port'].choices = () self.fields['power_port'].widget.attrs['disabled'] = True class InterfaceTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=InterfaceTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(InterfaceTypeChoices), required=False ) enabled = forms.NullBooleanField( label=_('Enabled'), required=False, widget=BulkEditNullBooleanSelect ) mgmt_only = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label=_('Management only') ) description = forms.CharField( label=_('Description'), required=False ) poe_mode = forms.ChoiceField( choices=add_blank_choice(InterfacePoEModeChoices), required=False, initial='', label=_('PoE mode') ) poe_type = forms.ChoiceField( choices=add_blank_choice(InterfacePoETypeChoices), required=False, initial='', label=_('PoE type') ) rf_role = forms.ChoiceField( choices=add_blank_choice(WirelessRoleChoices), required=False, initial='', label=_('Wireless role') ) nullable_fields = ('label', 'description', 'poe_mode', 'poe_type', 'rf_role') class FrontPortTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=FrontPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(PortTypeChoices), required=False ) color = ColorField( label=_('Color'), required=False ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('description',) class RearPortTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=RearPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(PortTypeChoices), required=False ) color = ColorField( label=_('Color'), required=False ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('description',) class ModuleBayTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=ModuleBayTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('label', 'position', 'description') class DeviceBayTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=DeviceBayTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) description = forms.CharField( label=_('Description'), required=False ) nullable_fields = ('label', 'description') class InventoryItemTemplateBulkEditForm(ComponentTemplateBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=InventoryItemTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( label=_('Label'), max_length=64, required=False ) description = forms.CharField( label=_('Description'), required=False ) role = DynamicModelChoiceField( label=_('Role'), queryset=InventoryItemRole.objects.all(), required=False ) manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description') # # Device components # class ComponentBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm): device = forms.ModelChoiceField( label=_('Device'), queryset=Device.objects.all(), required=False, disabled=True, widget=forms.HiddenInput() ) module = forms.ModelChoiceField( label=_('Module'), queryset=Module.objects.all(), required=False ) def __init__(self, *args, initial=None, **kwargs): try: self.device_id = int(initial.get('device')) except (TypeError, ValueError): self.device_id = None super().__init__(*args, initial=initial, **kwargs) # Limit module queryset to Modules which belong to the parent Device if self.device_id: device = Device.objects.filter(pk=self.device_id).first() self.fields['module'].queryset = Module.objects.filter(device=device) else: self.fields['module'].choices = () self.fields['module'].widget.attrs['disabled'] = True class ConsolePortBulkEditForm( ComponentBulkEditForm, form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description']) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) model = ConsolePort fieldsets = ( FieldSet('module', 'type', 'label', 'speed', 'description', 'mark_connected'), ) nullable_fields = ('module', 'label', 'description') class ConsoleServerPortBulkEditForm( ComponentBulkEditForm, form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description']) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) model = ConsoleServerPort fieldsets = ( FieldSet('module', 'type', 'label', 'speed', 'description', 'mark_connected'), ) nullable_fields = ('module', 'label', 'description') class PowerPortBulkEditForm( ComponentBulkEditForm, form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description']) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) model = PowerPort fieldsets = ( FieldSet('module', 'type', 'label', 'description', 'mark_connected'), FieldSet('maximum_draw', 'allocated_draw', name=_('Power')), ) nullable_fields = ('module', 'label', 'description', 'maximum_draw', 'allocated_draw') class PowerOutletBulkEditForm( ComponentBulkEditForm, form_from_model( PowerOutlet, ['label', 'type', 'status', 'color', 'feed_leg', 'power_port', 'mark_connected', 'description'] ) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) model = PowerOutlet fieldsets = ( FieldSet('module', 'type', 'label', 'status', 'description', 'mark_connected', 'color'), FieldSet('feed_leg', 'power_port', name=_('Power')), ) nullable_fields = ('module', 'label', 'type', 'feed_leg', 'power_port', 'description') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit power_port queryset to PowerPorts which belong to the parent Device if self.device_id: device = Device.objects.filter(pk=self.device_id).first() self.fields['power_port'].queryset = PowerPort.objects.filter(device=device) else: self.fields['power_port'].choices = () self.fields['power_port'].widget.attrs['disabled'] = True class InterfaceBulkEditForm( ComponentBulkEditForm, form_from_model(Interface, [ 'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans', 'vlan_translation_policy' ]) ): enabled = forms.NullBooleanField( label=_('Enabled'), required=False, widget=BulkEditNullBooleanSelect ) parent = DynamicModelChoiceField( label=_('Parent'), queryset=Interface.objects.all(), required=False, query_params={ 'virtual_chassis_member_id': '$device', } ) bridge = DynamicModelChoiceField( label=_('Bridge'), queryset=Interface.objects.all(), required=False, query_params={ 'virtual_chassis_member_id': '$device', } ) lag = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, query_params={ 'type': 'lag', 'virtual_chassis_member_id': '$device', }, label=_('LAG') ) vdcs = DynamicModelMultipleChoiceField( queryset=VirtualDeviceContext.objects.all(), required=False, label=_('Virtual device contexts'), query_params={ 'device_id': '$device', } ) speed = forms.IntegerField( label=_('Speed'), required=False, widget=NumberWithOptions( options=InterfaceSpeedChoices ) ) mgmt_only = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label=_('Management only') ) poe_mode = forms.ChoiceField( choices=add_blank_choice(InterfacePoEModeChoices), required=False, initial='', label=_('PoE mode') ) poe_type = forms.ChoiceField( choices=add_blank_choice(InterfacePoETypeChoices), required=False, initial='', label=_('PoE type') ) mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) mode = forms.ChoiceField( label=_('Mode'), choices=add_blank_choice(InterfaceModeChoices), required=False, initial='' ) vlan_group = DynamicModelChoiceField( queryset=VLANGroup.objects.all(), required=False, label=_('VLAN group') ) untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, query_params={ 'group_id': '$vlan_group', 'available_on_device': '$device', }, label=_('Untagged VLAN') ) tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, query_params={ 'group_id': '$vlan_group', 'available_on_device': '$device', }, label=_('Tagged VLANs') ) add_tagged_vlans = DynamicModelMultipleChoiceField( label=_('Add tagged VLANs'), queryset=VLAN.objects.all(), required=False, query_params={ 'group_id': '$vlan_group', 'available_on_device': '$device', }, ) remove_tagged_vlans = DynamicModelMultipleChoiceField( label=_('Remove tagged VLANs'), queryset=VLAN.objects.all(), required=False, query_params={ 'group_id': '$vlan_group', 'available_on_device': '$device', } ) qinq_svlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, label=_('Q-in-Q Service VLAN'), query_params={ 'group_id': '$vlan_group', 'available_on_device': '$device', 'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE, } ) vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label=_('VRF') ) wireless_lan_group = DynamicModelChoiceField( queryset=WirelessLANGroup.objects.all(), required=False, label=_('Wireless LAN group') ) wireless_lans = DynamicModelMultipleChoiceField( queryset=WirelessLAN.objects.all(), required=False, label=_('Wireless LANs'), query_params={ 'group_id': '$wireless_lan_group', } ) model = Interface fieldsets = ( FieldSet('module', 'type', 'label', 'speed', 'duplex', 'description'), FieldSet('vrf', 'wwn', name=_('Addressing')), FieldSet('vdcs', 'mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected', name=_('Operation')), FieldSet('poe_mode', 'poe_type', name=_('PoE')), FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')), FieldSet( 'mode', 'vlan_group', 'untagged_vlan', 'qinq_svlan', 'vlan_translation_policy', name=_('802.1Q Switching') ), FieldSet( TabbedGroups( FieldSet('tagged_vlans', name=_('Assignment')), FieldSet('add_tagged_vlans', 'remove_tagged_vlans', name=_('Add/Remove')), ), ), FieldSet( 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans', name=_('Wireless') ), ) nullable_fields = ( 'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'vdcs', 'mtu', 'description', 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vrf', 'wireless_lans', 'vlan_translation_policy', ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not self.device_id: # See #4523 if 'pk' in self.initial: site = None interfaces = Interface.objects.filter(pk__in=self.initial['pk']).prefetch_related('device__site') # Check interface sites. First interface should set site, further interfaces will either continue the # loop or reset back to no site and break the loop. for interface in interfaces: if site is None: site = interface.device.site elif interface.device.site is not site: site = None break if site is not None: # Query for VLANs assigned to the same site and VLANs with no site assigned (null). self.fields['untagged_vlan'].widget.add_query_param( 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE] ) self.fields['tagged_vlans'].widget.add_query_param( 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE] ) self.fields['add_tagged_vlans'].widget.add_query_param( 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE] ) self.fields['remove_tagged_vlans'].widget.add_query_param( 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE] ) self.fields['parent'].choices = () self.fields['parent'].widget.attrs['disabled'] = True self.fields['bridge'].choices = () self.fields['bridge'].widget.attrs['disabled'] = True self.fields['lag'].choices = () self.fields['lag'].widget.attrs['disabled'] = True def clean(self): super().clean() if not self.cleaned_data['mode']: if self.cleaned_data['untagged_vlan']: raise forms.ValidationError({'untagged_vlan': _("Interface mode must be specified to assign VLANs")}) elif self.cleaned_data['tagged_vlans']: raise forms.ValidationError({'tagged_vlans': _("Interface mode must be specified to assign VLANs")}) # Untagged interfaces cannot be assigned tagged VLANs elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and self.cleaned_data['tagged_vlans']: raise forms.ValidationError({ 'mode': _("An access interface cannot have tagged VLANs assigned.") }) # Remove all tagged VLAN assignments from "tagged all" interfaces elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL: self.cleaned_data['tagged_vlans'] = [] class FrontPortBulkEditForm( ComponentBulkEditForm, form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) model = FrontPort fieldsets = ( FieldSet('module', 'type', 'label', 'color', 'description', 'mark_connected'), ) nullable_fields = ('module', 'label', 'description', 'color') class RearPortBulkEditForm( ComponentBulkEditForm, form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), required=False, widget=BulkEditNullBooleanSelect ) model = RearPort fieldsets = ( FieldSet('module', 'type', 'label', 'color', 'description', 'mark_connected'), ) nullable_fields = ('module', 'label', 'description', 'color') class ModuleBayBulkEditForm( form_from_model(ModuleBay, ['label', 'position', 'description']), NetBoxModelBulkEditForm ): model = ModuleBay fieldsets = ( FieldSet('label', 'position', 'description'), ) nullable_fields = ('label', 'position', 'description') class DeviceBayBulkEditForm( form_from_model(DeviceBay, ['label', 'description']), NetBoxModelBulkEditForm ): model = DeviceBay fieldsets = ( FieldSet('label', 'description'), ) nullable_fields = ('label', 'description') class InventoryItemBulkEditForm( form_from_model(InventoryItem, ['label', 'role', 'manufacturer', 'part_id', 'description']), NetBoxModelBulkEditForm ): device = DynamicModelChoiceField( label=_('Device'), queryset=Device.objects.all(), required=False ) role = DynamicModelChoiceField( label=_('Role'), queryset=InventoryItemRole.objects.all(), required=False ) manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), required=False ) status = forms.ChoiceField( label=_('Status'), choices=add_blank_choice(InventoryItemStatusChoices), required=False, initial='' ) model = InventoryItem fieldsets = ( FieldSet('device', 'label', 'role', 'manufacturer', 'part_id', 'status', 'description'), ) nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Remove parent device passed as context to avoid conflicts with the actual device field # on this form (see bug #19464) self.initial.pop('device', None) # # Device component roles # class InventoryItemRoleBulkEditForm(OrganizationalModelBulkEditForm): color = ColorField( label=_('Color'), required=False ) model = InventoryItemRole fieldsets = ( FieldSet('color', 'description'), ) nullable_fields = ('color', 'description', 'comments') class VirtualDeviceContextBulkEditForm(PrimaryModelBulkEditForm): device = DynamicModelChoiceField( label=_('Device'), queryset=Device.objects.all(), required=False ) status = forms.ChoiceField( label=_('Status'), required=False, choices=add_blank_choice(VirtualDeviceContextStatusChoices) ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False ) model = VirtualDeviceContext fieldsets = ( FieldSet('device', 'status', 'tenant'), ) nullable_fields = ('device', 'tenant', ) # # Addressing # class MACAddressBulkEditForm(PrimaryModelBulkEditForm): model = MACAddress fieldsets = ( FieldSet('description'), ) nullable_fields = ('description', 'comments')