content: from django import forms
  from django.contrib.contenttypes.models import ContentType
  from django.core.validators import EMPTY_VALUES
  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, IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
  from netbox.forms import NetBoxModelForm
  from netbox.forms.mixins import ChangelogMessageMixin
  from tenancy.forms import TenancyForm
  from users.models import User
  from utilities.forms import add_blank_choice, get_field_value
  from utilities.forms.fields import (
      CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SlugField,
  )
  from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
  from utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK
  from utilities.jsonschema import JSONSchemaProperty
  from virtualization.models import Cluster, VMInterface
  from wireless.models import WirelessLAN, WirelessLANGroup
  from .common import InterfaceCommonForm, ModuleCommonForm
  
  __all__ = (
      'CableForm',
      'ConsolePortForm',
      'ConsolePortTemplateForm',
      'ConsoleServerPortForm',
      'ConsoleServerPortTemplateForm',
      'DeviceBayForm',
      'DeviceBayTemplateForm',
      'DeviceForm',
      'DeviceRoleForm',
      'DeviceTypeForm',
      'DeviceVCMembershipForm',
      'FrontPortForm',
      'FrontPortTemplateForm',
      'InterfaceForm',
      'InterfaceTemplateForm',
      'InventoryItemForm',
      'InventoryItemRoleForm',
      'InventoryItemTemplateForm',
      'LocationForm',
      'MACAddressForm',
      'ManufacturerForm',
      'ModuleForm',
      'ModuleBayForm',
      'ModuleBayTemplateForm',
      'ModuleTypeForm',
      'ModuleTypeProfileForm',
      'PlatformForm',
      'PopulateDeviceBayForm',
      'PowerFeedForm',
      'PowerOutletForm',
      'PowerOutletTemplateForm',
      'PowerPanelForm',
      'PowerPortForm',
      'PowerPortTemplateForm',
      'RackForm',
      'RackReservationForm',
      'RackRoleForm',
      'RackTypeForm',
      'RearPortForm',
      'RearPortTemplateForm',
      'RegionForm',
      'SiteForm',
      'SiteGroupForm',
      'VCMemberSelectForm',
      'VirtualChassisForm',
      'VirtualDeviceContextForm'
  )
  
  
  class RegionForm(NetBoxModelForm):
      parent = DynamicModelChoiceField(
          label=_('Parent'),
          queryset=Region.objects.all(),
          required=False
      )
      slug = SlugField()
      comments = CommentField()
  
      fieldsets = (
          FieldSet('parent', 'name', 'slug', 'description', 'tags'),
      )
  
      class Meta:
          model = Region
          fields = (
              'parent', 'name', 'slug', 'description', 'tags', 'comments',
          )
  
  
  class SiteGroupForm(NetBoxModelForm):
      parent = DynamicModelChoiceField(
          label=_('Parent'),
          queryset=SiteGroup.objects.all(),
          required=False
      )
      slug = SlugField()
      comments = CommentField()
  
      fieldsets = (
          FieldSet('parent', 'name', 'slug', 'description', 'tags'),
      )
  
      class Meta:
          model = SiteGroup
          fields = (
              'parent', 'name', 'slug', 'description', 'comments', 'tags',
          )
  
  
  class SiteForm(TenancyForm, NetBoxModelForm):
      region = DynamicModelChoiceField(
          label=_('Region'),
          queryset=Region.objects.all(),
          required=False,
          quick_add=True
      )
      group = DynamicModelChoiceField(
          label=_('Group'),
          queryset=SiteGroup.objects.all(),
          required=False,
          quick_add=True
      )
      asns = DynamicModelMultipleChoiceField(
          queryset=ASN.objects.all(),
          label=_('ASNs'),
          required=False
      )
      slug = SlugField()
      time_zone = TimeZoneFormField(
          label=_('Time zone'),
          choices=add_blank_choice(TimeZoneFormField().choices),
          required=False
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet(
              'name', 'slug', 'status', 'region', 'group', 'facility', 'asns', 'time_zone', 'description', 'tags',
              name=_('Site')
          ),
          FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
          FieldSet('physical_address', 'shipping_address', 'latitude', 'longitude', name=_('Contact Info')),
      )
  
      class Meta:
          model = Site
          fields = (
              'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asns', 'time_zone',
              'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
          )
          widgets = {
              'physical_address': forms.Textarea(
                  attrs={
                      'rows': 3,
                  }
              ),
              'shipping_address': forms.Textarea(
                  attrs={
                      'rows': 3,
                  }
              ),
          }
  
  
  class LocationForm(TenancyForm, NetBoxModelForm):
      site = DynamicModelChoiceField(
          label=_('Site'),
          queryset=Site.objects.all(),
          selector=True
      )
      parent = DynamicModelChoiceField(
          label=_('Parent'),
          queryset=Location.objects.all(),
          required=False,
          query_params={
              'site_id': '$site'
          }
      )
      slug = SlugField()
      comments = CommentField()
  
      fieldsets = (
          FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')),
          FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
      )
  
      class Meta:
          model = Location
          fields = (
              'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
              'facility', 'tags', 'comments',
          )
  
  
  class RackRoleForm(NetBoxModelForm):
      slug = SlugField()
  
      fieldsets = (
          FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Rack Role')),
      )
  
      class Meta:
          model = RackRole
          fields = [
              'name', 'slug', 'color', 'description', 'tags',
          ]
  
  
  class RackTypeForm(NetBoxModelForm):
      manufacturer = DynamicModelChoiceField(
          label=_('Manufacturer'),
          queryset=Manufacturer.objects.all(),
          quick_add=True
      )
      comments = CommentField()
      slug = SlugField(
          label=_('Slug'),
          slug_source='model'
      )
  
      fieldsets = (
          FieldSet('manufacturer', 'model', 'slug', 'description', 'form_factor', 'tags', name=_('Rack Type')),
          FieldSet(
              'width', 'u_height',
              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')),
      )
  
      class Meta:
          model = RackType
          fields = [
              'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units',
              'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight',
              'weight_unit', 'description', 'comments', 'tags',
          ]
  
  
  class RackForm(TenancyForm, NetBoxModelForm):
      site = DynamicModelChoiceField(
          label=_('Site'),
          queryset=Site.objects.all(),
          selector=True
      )
      location = DynamicModelChoiceField(
          label=_('Location'),
          queryset=Location.objects.all(),
          required=False,
          query_params={
              'site_id': '$site'
          }
      )
      role = DynamicModelChoiceField(
          label=_('Role'),
          queryset=RackRole.objects.all(),
          required=False
      )
      rack_type = DynamicModelChoiceField(
          label=_('Rack Type'),
          queryset=RackType.objects.all(),
          required=False,
          selector=True,
          help_text=_("Select a pre-defined rack type, or set physical characteristics below."),
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet(
              'site', 'location', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags',
              name=_('Rack')
          ),
          FieldSet('facility_id', 'serial', 'asset_tag', name=_('Inventory Control')),
          FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
      )
  
      class Meta:
          model = Rack
          fields = [
              'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
              'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
              'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight',
              'weight_unit', 'description', 'comments', 'tags',
          ]
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          # Mimic HTMXSelect()
          self.fields['rack_type'].widget.attrs.update({
              'hx-get': '.',
              'hx-include': '#form_fields',
              'hx-target': '#form_fields',
          })
  
          # Omit RackType-defined fields if rack_type is set
          if get_field_value(self, 'rack_type'):
              for field_name in Rack.RACKTYPE_FIELDS:
                  del self.fields[field_name]
          else:
              self.fieldsets = (
                  *self.fieldsets,
                  FieldSet(
                      'form_factor', 'width', 'starting_unit', 'u_height',
                      InlineFields('outer_width', 'outer_height', 'outer_depth', 'outer_unit',
                                   label=_('Outer Dimensions')),
                      InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
                      'mounting_depth', 'desc_units', name=_('Dimensions')
                  ),
              )
  
  
  class RackReservationForm(TenancyForm, NetBoxModelForm):
      rack = DynamicModelChoiceField(
          label=_('Rack'),
          queryset=Rack.objects.all(),
          selector=True
      )
      units = NumericArrayField(
          label=_('Units'),
          base_field=forms.IntegerField(),
          help_text=_("Comma-separated list of numeric unit IDs. A range may be specified using a hyphen.")
      )
      user = forms.ModelChoiceField(
          label=_('User'),
          queryset=User.objects.order_by('username')
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet('rack', 'units', 'status', 'user', 'description', 'tags', name=_('Reservation')),
          FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
      )
  
      class Meta:
          model = RackReservation
          fields = [
              'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
          ]
  
  
  class ManufacturerForm(NetBoxModelForm):
      slug = SlugField()
  
      fieldsets = (
          FieldSet('name', 'slug', 'description', 'tags', name=_('Manufacturer')),
      )
  
      class Meta:
          model = Manufacturer
          fields = [
              'name', 'slug', 'description', 'tags',
          ]
  
  
  class DeviceTypeForm(NetBoxModelForm):
      manufacturer = DynamicModelChoiceField(
          label=_('Manufacturer'),
          queryset=Manufacturer.objects.all(),
          quick_add=True
      )
      default_platform = DynamicModelChoiceField(
          label=_('Default platform'),
          queryset=Platform.objects.all(),
          required=False,
          selector=True,
          query_params={
              'manufacturer_id': ['$manufacturer', 'null'],
          }
      )
      slug = SlugField(
          label=_('Slug'),
          slug_source='model'
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet('manufacturer', 'model', 'slug', 'default_platform', 'description', 'tags', name=_('Device Type')),
          FieldSet(
              'u_height', 'exclude_from_utilization', 'is_full_depth', 'part_number', 'subdevice_role', 'airflow',
              'weight', 'weight_unit', name=_('Chassis')
          ),
          FieldSet('front_image', 'rear_image', name=_('Images')),
      )
  
      class Meta:
          model = DeviceType
          fields = [
              'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization',
              'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
              'description', 'comments', 'tags',
          ]
          widgets = {
              'front_image': ClearableFileInput(attrs={
                  'accept': DEVICETYPE_IMAGE_FORMATS
              }),
              'rear_image': ClearableFileInput(attrs={
                  'accept': DEVICETYPE_IMAGE_FORMATS
              }),
          }
  
  
  class ModuleTypeProfileForm(NetBoxModelForm):
      schema = JSONField(
          label=_('Schema'),
          required=False,
          help_text=_("Enter a valid JSON schema to define supported attributes.")
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet('name', 'description', 'schema', 'tags', name=_('Profile')),
      )
  
      class Meta:
          model = ModuleTypeProfile
          fields = [
              'name', 'description', 'schema', 'comments', 'tags',
          ]
  
  
  class ModuleTypeForm(NetBoxModelForm):
      profile = forms.ModelChoiceField(
          queryset=ModuleTypeProfile.objects.all(),
          label=_('Profile'),
          required=False,
          widget=HTMXSelect()
      )
      manufacturer = DynamicModelChoiceField(
          label=_('Manufacturer'),
          queryset=Manufacturer.objects.all()
      )
      comments = CommentField()
  
      @property
      def fieldsets(self):
          return [
              FieldSet('manufacturer', 'model', 'part_number', 'description', 'tags', name=_('Module Type')),
              FieldSet('airflow', 'weight', 'weight_unit', name=_('Hardware')),
              FieldSet('profile', *self.attr_fields, name=_('Profile & Attributes'))
          ]
  
      class Meta:
          model = ModuleType
          fields = [
              'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit',
              'comments', 'tags',
          ]
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          # Track profile-specific attribute fields
          self.attr_fields = []
  
          # Retrieve assigned ModuleTypeProfile, if any
          if not (profile_id := get_field_value(self, 'profile')):
              return
          if not (profile := ModuleTypeProfile.objects.filter(pk=profile_id).first()):
              return
  
          # Extend form with fields for profile attributes
          for attr, form_field in self._get_attr_form_fields(profile).items():
              field_name = f'attr_{attr}'
              self.attr_fields.append(field_name)
              self.fields[field_name] = form_field
              if self.instance.attribute_data:
                  self.fields[field_name].initial = self.instance.attribute_data.get(attr)
  
      @staticmethod
      def _get_attr_form_fields(profile):
          """
          Return a dictionary mapping of attribute names to form fields, suitable for extending
          the form per the selected ModuleTypeProfile.
          """
          if not profile.schema:
              return {}
  
          properties = profile.schema.get('properties', {})
          required_fields = profile.schema.get('required', [])
  
          attr_fields = {}
          for name, options in properties.items():
              prop = JSONSchemaProperty(**options)
              attr_fields[name] = prop.to_form_field(name, required=name in required_fields)
  
          return dict(sorted(attr_fields.items()))
  
      def _post_clean(self):
  
          # Compile attribute data from the individual form fields
          if self.cleaned_data.get('profile'):
              self.instance.attribute_data = {
                  name[5:]: self.cleaned_data[name]  # Remove the attr_ prefix
                  for name in self.attr_fields
                  if self.cleaned_data.get(name) not in EMPTY_VALUES
              }
  
          return super()._post_clean()
  
  
  class DeviceRoleForm(NetBoxModelForm):
      config_template = DynamicModelChoiceField(
          label=_('Config template'),
          queryset=ConfigTemplate.objects.all(),
          required=False
      )
      slug = SlugField()
      parent = DynamicModelChoiceField(
          label=_('Parent'),
          queryset=DeviceRole.objects.all(),
          required=False,
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet(
              'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description',
              'tags', name=_('Device Role')
          ),
      )
  
      class Meta:
          model = DeviceRole
          fields = [
              'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags',
          ]
  
  
  class PlatformForm(NetBoxModelForm):
      parent = DynamicModelChoiceField(
          label=_('Parent'),
          queryset=Platform.objects.all(),
          required=False,
      )
      manufacturer = DynamicModelChoiceField(
          label=_('Manufacturer'),
          queryset=Manufacturer.objects.all(),
          required=False,
          quick_add=True
      )
      config_template = DynamicModelChoiceField(
          label=_('Config template'),
          queryset=ConfigTemplate.objects.all(),
          required=False
      )
      slug = SlugField(
          label=_('Slug'),
          max_length=64
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet(
              'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'tags', name=_('Platform'),
          ),
      )
  
      class Meta:
          model = Platform
          fields = [
              'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'comments', 'tags',
          ]
  
  
  class DeviceForm(TenancyForm, NetBoxModelForm):
      site = DynamicModelChoiceField(
          label=_('Site'),
          queryset=Site.objects.all(),
          selector=True
      )
      location = DynamicModelChoiceField(
          label=_('Location'),
          queryset=Location.objects.all(),
          required=False,
          query_params={
              'site_id': '$site'
          },
          initial_params={
              'racks': '$rack'
          }
      )
      rack = DynamicModelChoiceField(
          label=_('Rack'),
          queryset=Rack.objects.all(),
          required=False,
          query_params={
              'site_id': '$site',
              'location_id': '$location',
          }
      )
      position = forms.DecimalField(
          label=_('Position'),
          required=False,
          help_text=_("The lowest-numbered unit occupied by the device"),
          localize=True,
          widget=APISelect(
              api_url='/api/dcim/racks/{{rack}}/elevation/',
              attrs={
                  'ts-disabled-field': 'device',
                  'data-dynamic-params': '[{"fieldName":"face","queryParam":"face"}]'
              },
          )
      )
      device_type = DynamicModelChoiceField(
          label=_('Device type'),
          queryset=DeviceType.objects.all(),
          context={
              'parent': 'manufacturer',
          },
          selector=True
      )
      role = DynamicModelChoiceField(
          label=_('Device role'),
          queryset=DeviceRole.objects.all(),
          quick_add=True
      )
      platform = DynamicModelChoiceField(
          label=_('Platform'),
          queryset=Platform.objects.all(),
          required=False,
          selector=True,
          query_params={
              'available_for_device_type': '$device_type',
          }
      )
      cluster = DynamicModelChoiceField(
          label=_('Cluster'),
          queryset=Cluster.objects.all(),
          required=False,
          selector=True,
          query_params={
              'site_id': ['$site', 'null']
          },
      )
      comments = CommentField()
      local_context_data = JSONField(
          required=False,
          label=''
      )
      virtual_chassis = DynamicModelChoiceField(
          label=_('Virtual chassis'),
          queryset=VirtualChassis.objects.all(),
          required=False,
          context={
              'parent': 'master',
          },
          selector=True
      )
      vc_position = forms.IntegerField(
          required=False,
          label=_('Position'),
          help_text=_("The position in the virtual chassis this device is identified by")
      )
      vc_priority = forms.IntegerField(
          required=False,
          label=_('Priority'),
          help_text=_("The priority of the device in the virtual chassis")
      )
      config_template = DynamicModelChoiceField(
          label=_('Config template'),
          queryset=ConfigTemplate.objects.all(),
          required=False
      )
  
      class Meta:
          model = Device
          fields = [
              'name', 'role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'location', 'position', 'face',
              'latitude', 'longitude', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster',
              'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority', 'description', 'config_template',
              'comments', 'tags', 'local_context_data',
          ]
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          if self.instance.pk:
  
              # Compile list of choices for primary IPv4 and IPv6 addresses
              oob_ip_choices = [(None, '---------')]
              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))
                      oob_ip_choices.extend(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
              self.fields['oob_ip'].choices = oob_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)
  
              # 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
              self.fields['oob_ip'].choices = []
              self.fields['oob_ip'].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 ModuleForm(ModuleCommonForm, NetBoxModelForm):
      device = DynamicModelChoiceField(
          label=_('Device'),
          queryset=Device.objects.all(),
          initial_params={
              'modulebays': '$module_bay'
          }
      )
      module_bay = DynamicModelChoiceField(
          label=_('Module bay'),
          queryset=ModuleBay.objects.all(),
          query_params={
              'device_id': '$device'
          },
          context={
              'disabled': 'installed_module',
          },
      )
      module_type = DynamicModelChoiceField(
          label=_('Module type'),
          queryset=ModuleType.objects.all(),
          context={
              'parent': 'manufacturer',
          },
          selector=True
      )
      comments = CommentField()
      replicate_components = forms.BooleanField(
          label=_('Replicate components'),
          required=False,
          initial=True,
          help_text=_("Automatically populate components associated with this module type")
      )
      adopt_components = forms.BooleanField(
          label=_('Adopt components'),
          required=False,
          initial=False,
          help_text=_("Adopt already existing components")
      )
  
      fieldsets = (
          FieldSet('device', 'module_bay', 'module_type', 'status', 'description', 'tags', name=_('Module')),
          FieldSet('serial', 'asset_tag', 'replicate_components', 'adopt_components', name=_('Hardware')),
      )
  
      class Meta:
          model = Module
          fields = [
              'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag', 'tags', 'replicate_components',
              'adopt_components', 'description', 'comments',
          ]
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          if self.instance.pk:
              self.fields['device'].disabled = True
              self.fields['replicate_components'].initial = False
              self.fields['replicate_components'].disabled = True
              self.fields['adopt_components'].initial = False
              self.fields['adopt_components'].disabled = True
  
  
  def get_termination_type_choices():
      return add_blank_choice([
          (f'{ct.app_label}.{ct.model}', ct.model_class()._meta.verbose_name.title())
          for ct in ContentType.objects.filter(CABLE_TERMINATION_MODELS)
      ])
  
  
  class CableForm(TenancyForm, NetBoxModelForm):
      a_terminations_type = forms.ChoiceField(
          choices=get_termination_type_choices,
          required=False,
          widget=HTMXSelect(),
          label=_('Type')
      )
      b_terminations_type = forms.ChoiceField(
          choices=get_termination_type_choices,
          required=False,
          widget=HTMXSelect(),
          label=_('Type')
      )
      comments = CommentField()
  
      class Meta:
          model = Cable
          fields = [
              'a_terminations_type', 'b_terminations_type', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color',
              'length', 'length_unit', 'description', 'comments', 'tags',
          ]
  
  
  class PowerPanelForm(NetBoxModelForm):
      site = DynamicModelChoiceField(
          label=_('Site'),
          queryset=Site.objects.all(),
          selector=True
      )
      location = DynamicModelChoiceField(
          label=_('Location'),
          queryset=Location.objects.all(),
          required=False,
          query_params={
              'site_id': '$site'
          }
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet('site', 'location', 'name', 'description', 'tags', name=_('Power Panel')),
      )
  
      class Meta:
          model = PowerPanel
          fields = [
              'site', 'location', 'name', 'description', 'comments', 'tags',
          ]
  
  
  class PowerFeedForm(TenancyForm, NetBoxModelForm):
      power_panel = DynamicModelChoiceField(
          label=_('Power panel'),
          queryset=PowerPanel.objects.all(),
          selector=True,
          quick_add=True
      )
      rack = DynamicModelChoiceField(
          label=_('Rack'),
          queryset=Rack.objects.all(),
          required=False,
          selector=True
      )
      comments = CommentField()
  
      fieldsets = (
          FieldSet(
              'power_panel', 'rack', 'name', 'status', 'type', 'description', 'mark_connected', 'tags',
              name=_('Power Feed')
          ),
          FieldSet('supply', 'voltage', 'amperage', 'phase', 'max_utilization', name=_('Characteristics')),
          FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
      )
  
      class Meta:
          model = PowerFeed
          fields = [
              'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
              'max_utilization', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
          ]
  
  
  #
  # Virtual chassis
  #
  
  class VirtualChassisForm(NetBoxModelForm):
      master = forms.ModelChoiceField(
          label=_('Master'),
          queryset=Device.objects.all(),
          required=False,
      )
      comments = CommentField()
  
      class Meta:
          model = VirtualChassis
          fields = [
              'name', 'domain', 'master', 'description', 'comments', '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(forms.Form):
      device = DynamicModelChoiceField(
          label=_('Device'),
          queryset=Device.objects.all(),
          query_params={
              'virtual_chassis_id': 'null',
          },
          selector=True
      )
  
      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 ComponentTemplateForm(ChangelogMessageMixin, forms.ModelForm):
      device_type = DynamicModelChoiceField(
          label=_('Device type'),
          queryset=DeviceType.objects.all(),
          context={
              'parent': 'manufacturer',
          }
      )
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          # Disable reassignment of DeviceType when editing an existing instance
          if self.instance.pk:
              self.fields['device_type'].disabled = True
  
  
  class ModularComponentTemplateForm(ComponentTemplateForm):
      device_type = DynamicModelChoiceField(
          label=_('Device type'),
          queryset=DeviceType.objects.all(),
          required=False,
          context={
              'parent': 'manufacturer',
          }
      )
      module_type = DynamicModelChoiceField(
          label=_('Module type'),
          queryset=ModuleType.objects.all(),
          required=False,
          context={
              'parent': 'manufacturer',
          }
      )
  
      fieldsets = (
          FieldSet(
  