| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518 |
- from django import forms
- from django.utils.translation import gettext as _
- 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 ipam.models import ASN, IPAddress, VLAN, VLANGroup, VRF
- from netbox.forms import NetBoxModelForm
- from tenancy.forms import TenancyForm
- from utilities.forms import (
- APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField,
- DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SelectWithPK, SmallTextarea,
- SlugField, StaticSelect, SelectSpeedWidget,
- )
- from virtualization.models import Cluster, ClusterGroup
- from wireless.models import WirelessLAN, WirelessLANGroup
- from .common import InterfaceCommonForm
- __all__ = (
- 'CableForm',
- 'ConsolePortForm',
- 'ConsolePortTemplateForm',
- 'ConsoleServerPortForm',
- 'ConsoleServerPortTemplateForm',
- 'DeviceBayForm',
- 'DeviceBayTemplateForm',
- 'DeviceForm',
- 'DeviceRoleForm',
- 'DeviceTypeForm',
- 'DeviceVCMembershipForm',
- 'FrontPortForm',
- 'FrontPortTemplateForm',
- 'InterfaceForm',
- 'InterfaceTemplateForm',
- 'InventoryItemForm',
- 'InventoryItemRoleForm',
- 'InventoryItemTemplateForm',
- 'LocationForm',
- 'ManufacturerForm',
- 'ModuleForm',
- 'ModuleBayForm',
- 'ModuleBayTemplateForm',
- 'ModuleTypeForm',
- '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<br />
- Tagged: One untagged VLAN and/or one or more tagged VLANs<br />
- Tagged (All): Implies all VLANs are available (w/optional untagged VLAN)
- """
- class RegionForm(NetBoxModelForm):
- parent = DynamicModelChoiceField(
- queryset=Region.objects.all(),
- required=False
- )
- slug = SlugField()
- class Meta:
- model = Region
- fields = (
- 'parent', 'name', 'slug', 'description', 'tags',
- )
- class SiteGroupForm(NetBoxModelForm):
- parent = DynamicModelChoiceField(
- queryset=SiteGroup.objects.all(),
- required=False
- )
- slug = SlugField()
- class Meta:
- model = SiteGroup
- fields = (
- 'parent', 'name', 'slug', 'description', 'tags',
- )
- class SiteForm(TenancyForm, NetBoxModelForm):
- region = DynamicModelChoiceField(
- queryset=Region.objects.all(),
- required=False
- )
- group = DynamicModelChoiceField(
- queryset=SiteGroup.objects.all(),
- required=False
- )
- asns = DynamicModelMultipleChoiceField(
- queryset=ASN.objects.all(),
- label=_('ASNs'),
- required=False
- )
- slug = SlugField()
- time_zone = TimeZoneFormField(
- choices=add_blank_choice(TimeZoneFormField().choices),
- required=False,
- widget=StaticSelect()
- )
- comments = CommentField()
- fieldsets = (
- ('Site', (
- 'name', 'slug', 'status', 'region', 'group', 'facility', 'asns', 'time_zone', 'description', 'tags',
- )),
- ('Tenancy', ('tenant_group', 'tenant')),
- ('Contact Info', ('physical_address', 'shipping_address', 'latitude', 'longitude')),
- )
- 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': 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)",
- '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(TenancyForm, NetBoxModelForm):
- 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()
- fieldsets = (
- ('Location', (
- 'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tags',
- )),
- ('Tenancy', ('tenant_group', 'tenant')),
- )
- class Meta:
- model = Location
- fields = (
- 'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
- )
- class RackRoleForm(NetBoxModelForm):
- slug = SlugField()
- class Meta:
- model = RackRole
- fields = [
- 'name', 'slug', 'color', 'description', 'tags',
- ]
- class RackForm(TenancyForm, NetBoxModelForm):
- 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()
- 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(TenancyForm, NetBoxModelForm):
- 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',
- }
- )
- location = DynamicModelChoiceField(
- queryset=Location.objects.all(),
- required=False,
- query_params={
- 'site_id': '$site'
- }
- )
- rack = DynamicModelChoiceField(
- queryset=Rack.objects.all(),
- query_params={
- 'site_id': '$site',
- 'location_id': '$location',
- }
- )
- 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()
- )
- fieldsets = (
- ('Reservation', ('region', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')),
- ('Tenancy', ('tenant_group', 'tenant')),
- )
- class Meta:
- model = RackReservation
- fields = [
- 'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant',
- 'description', 'tags',
- ]
- class ManufacturerForm(NetBoxModelForm):
- slug = SlugField()
- class Meta:
- model = Manufacturer
- fields = [
- 'name', 'slug', 'description', 'tags',
- ]
- class DeviceTypeForm(NetBoxModelForm):
- manufacturer = DynamicModelChoiceField(
- queryset=Manufacturer.objects.all()
- )
- slug = SlugField(
- slug_source='model'
- )
- comments = CommentField()
- fieldsets = (
- ('Device Type', (
- 'manufacturer', 'model', 'slug', 'part_number', 'tags',
- )),
- ('Chassis', (
- 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
- )),
- ('Images', ('front_image', 'rear_image')),
- )
- class Meta:
- model = DeviceType
- fields = [
- 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
- 'front_image', 'rear_image', 'comments', 'tags',
- ]
- widgets = {
- 'subdevice_role': StaticSelect(),
- 'front_image': ClearableFileInput(attrs={
- 'accept': DEVICETYPE_IMAGE_FORMATS
- }),
- 'rear_image': ClearableFileInput(attrs={
- 'accept': DEVICETYPE_IMAGE_FORMATS
- })
- }
- class ModuleTypeForm(NetBoxModelForm):
- manufacturer = DynamicModelChoiceField(
- queryset=Manufacturer.objects.all()
- )
- comments = CommentField()
- fieldsets = (
- ('Module Type', (
- 'manufacturer', 'model', 'part_number', 'tags',
- )),
- )
- class Meta:
- model = ModuleType
- fields = [
- 'manufacturer', 'model', 'part_number', 'comments', 'tags',
- ]
- class DeviceRoleForm(NetBoxModelForm):
- slug = SlugField()
- class Meta:
- model = DeviceRole
- fields = [
- 'name', 'slug', 'color', 'vm_role', 'description', 'tags',
- ]
- class PlatformForm(NetBoxModelForm):
- 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', 'tags',
- ]
- widgets = {
- 'napalm_args': SmallTextarea(),
- }
- class DeviceForm(TenancyForm, NetBoxModelForm):
- 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=''
- )
- virtual_chassis = DynamicModelChoiceField(
- queryset=VirtualChassis.objects.all(),
- required=False
- )
- vc_position = forms.IntegerField(
- required=False,
- help_text="The position in the virtual chassis this device is identified by"
- )
- vc_priority = forms.IntegerField(
- required=False,
- help_text="The priority of the device in the virtual chassis"
- )
- 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', 'virtual_chassis', 'vc_position', 'vc_priority',
- '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)
- # 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 ModuleForm(NetBoxModelForm):
- device = DynamicModelChoiceField(
- queryset=Device.objects.all(),
- initial_params={
- 'modulebays': '$module_bay'
- }
- )
- module_bay = DynamicModelChoiceField(
- queryset=ModuleBay.objects.all(),
- query_params={
- 'device_id': '$device'
- }
- )
- manufacturer = DynamicModelChoiceField(
- queryset=Manufacturer.objects.all(),
- required=False,
- initial_params={
- 'module_types': '$module_type'
- }
- )
- module_type = DynamicModelChoiceField(
- queryset=ModuleType.objects.all(),
- query_params={
- 'manufacturer_id': '$manufacturer'
- }
- )
- comments = CommentField()
- replicate_components = forms.BooleanField(
- required=False,
- initial=True,
- help_text="Automatically populate components associated with this module type"
- )
- adopt_components = forms.BooleanField(
- required=False,
- initial=False,
- help_text="Adopt already existing components"
- )
- fieldsets = (
- ('Module', (
- 'device', 'module_bay', 'manufacturer', 'module_type', 'tags',
- )),
- ('Hardware', (
- 'serial', 'asset_tag', 'replicate_components', 'adopt_components',
- )),
- )
- class Meta:
- model = Module
- fields = [
- 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', 'tags',
- 'replicate_components', 'adopt_components', 'comments',
- ]
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- if self.instance.pk:
- 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 save(self, *args, **kwargs):
- # If replicate_components is False, disable automatic component replication on the instance
- if self.instance.pk or not self.cleaned_data['replicate_components']:
- self.instance._disable_replication = True
- if self.cleaned_data['adopt_components']:
- self.instance._adopt_components = True
- return super().save(*args, **kwargs)
- def clean(self):
- super().clean()
- replicate_components = self.cleaned_data.get("replicate_components")
- adopt_components = self.cleaned_data.get("adopt_components")
- device = self.cleaned_data['device']
- module_type = self.cleaned_data['module_type']
- module_bay = self.cleaned_data['module_bay']
- # Bail out if we are not installing a new module or if we are not replicating components
- if self.instance.pk or not replicate_components:
- return
- for templates, component_attribute in [
- ("consoleporttemplates", "consoleports"),
- ("consoleserverporttemplates", "consoleserverports"),
- ("interfacetemplates", "interfaces"),
- ("powerporttemplates", "powerports"),
- ("poweroutlettemplates", "poweroutlets"),
- ("rearporttemplates", "rearports"),
- ("frontporttemplates", "frontports")
- ]:
- # Prefetch installed components
- installed_components = {
- component.name: component for component in getattr(device, component_attribute).all()
- }
- # Get the templates for the module type.
- for template in getattr(module_type, templates).all():
- # Installing modules with placeholders require that the bay has a position value
- if MODULE_TOKEN in template.name and not module_bay.position:
- raise forms.ValidationError(
- "Cannot install module with placeholder values in a module bay with no position defined"
- )
- resolved_name = template.name.replace(MODULE_TOKEN, module_bay.position)
- existing_item = installed_components.get(resolved_name)
- # It is not possible to adopt components already belonging to a module
- if adopt_components and existing_item and existing_item.module:
- raise forms.ValidationError(
- f"Cannot adopt {template.component_model.__name__} '{resolved_name}' as it already belongs "
- f"to a module"
- )
- # If we are not adopting components we error if the component exists
- if not adopt_components and resolved_name in installed_components:
- raise forms.ValidationError(
- f"{template.component_model.__name__} - {resolved_name} already exists"
- )
- class CableForm(TenancyForm, NetBoxModelForm):
- class Meta:
- model = Cable
- fields = [
- 'type', 'status', 'tenant_group', 'tenant', '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(NetBoxModelForm):
- 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'
- }
- )
- fieldsets = (
- ('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'tags')),
- )
- class Meta:
- model = PowerPanel
- fields = [
- 'region', 'site_group', 'site', 'location', 'name', 'tags',
- ]
- class PowerFeedForm(NetBoxModelForm):
- 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()
- fieldsets = (
- ('Power Panel', ('region', 'site', 'power_panel')),
- ('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')),
- ('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')),
- )
- 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',
- ]
- widgets = {
- 'status': StaticSelect(),
- 'type': StaticSelect(),
- 'supply': StaticSelect(),
- 'phase': StaticSelect(),
- }
- #
- # Virtual chassis
- #
- class VirtualChassisForm(NetBoxModelForm):
- master = forms.ModelChoiceField(
- queryset=Device.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', 'module_type', 'name', 'label', 'type', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect,
- }
- class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm):
- class Meta:
- model = ConsoleServerPortTemplate
- fields = [
- 'device_type', 'module_type', 'name', 'label', 'type', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect,
- }
- class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm):
- class Meta:
- model = PowerPortTemplate
- fields = [
- 'device_type', 'module_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm):
- power_port = DynamicModelChoiceField(
- queryset=PowerPortTemplate.objects.all(),
- required=False,
- query_params={
- 'devicetype_id': '$device_type',
- }
- )
- class Meta:
- model = PowerOutletTemplate
- fields = [
- 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect(),
- 'feed_leg': StaticSelect(),
- }
- class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm):
- class Meta:
- model = InterfaceTemplate
- fields = [
- 'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm):
- rear_port = DynamicModelChoiceField(
- queryset=RearPortTemplate.objects.all(),
- required=False,
- query_params={
- 'devicetype_id': '$device_type',
- }
- )
- class Meta:
- model = FrontPortTemplate
- fields = [
- 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
- 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class RearPortTemplateForm(BootstrapMixin, forms.ModelForm):
- class Meta:
- model = RearPortTemplate
- fields = [
- 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- 'module_type': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class ModuleBayTemplateForm(BootstrapMixin, forms.ModelForm):
- class Meta:
- model = ModuleBayTemplate
- fields = [
- 'device_type', 'name', 'label', 'position', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- }
- class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm):
- class Meta:
- model = DeviceBayTemplate
- fields = [
- 'device_type', 'name', 'label', 'description',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- }
- class InventoryItemTemplateForm(BootstrapMixin, forms.ModelForm):
- parent = DynamicModelChoiceField(
- queryset=InventoryItemTemplate.objects.all(),
- required=False,
- query_params={
- 'devicetype_id': '$device_type'
- }
- )
- role = DynamicModelChoiceField(
- queryset=InventoryItemRole.objects.all(),
- required=False
- )
- manufacturer = DynamicModelChoiceField(
- queryset=Manufacturer.objects.all(),
- required=False
- )
- component_type = ContentTypeChoiceField(
- queryset=ContentType.objects.all(),
- limit_choices_to=MODULAR_COMPONENT_TEMPLATE_MODELS,
- required=False,
- widget=forms.HiddenInput
- )
- component_id = forms.IntegerField(
- required=False,
- widget=forms.HiddenInput
- )
- class Meta:
- model = InventoryItemTemplate
- fields = [
- 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description',
- 'component_type', 'component_id',
- ]
- widgets = {
- 'device_type': forms.HiddenInput(),
- }
- #
- # Device components
- #
- class ConsolePortForm(NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- class Meta:
- model = ConsolePort
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- 'speed': StaticSelect(),
- }
- class ConsoleServerPortForm(NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- class Meta:
- model = ConsoleServerPort
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- 'speed': StaticSelect(),
- }
- class PowerPortForm(NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- class Meta:
- model = PowerPort
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected',
- 'description',
- 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class PowerOutletForm(NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- power_port = DynamicModelChoiceField(
- queryset=PowerPort.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- class Meta:
- model = PowerOutlet
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg', 'mark_connected', 'description',
- 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- 'feed_leg': StaticSelect(),
- }
- class InterfaceForm(InterfaceCommonForm, NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- parent = DynamicModelChoiceField(
- queryset=Interface.objects.all(),
- required=False,
- label='Parent interface',
- query_params={
- 'device_id': '$device',
- }
- )
- bridge = DynamicModelChoiceField(
- queryset=Interface.objects.all(),
- required=False,
- label='Bridged interface',
- query_params={
- 'device_id': '$device',
- }
- )
- lag = DynamicModelChoiceField(
- queryset=Interface.objects.all(),
- required=False,
- label='LAG interface',
- query_params={
- 'device_id': '$device',
- 'type': 'lag',
- }
- )
- 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',
- }
- )
- 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',
- 'available_on_device': '$device',
- }
- )
- tagged_vlans = DynamicModelMultipleChoiceField(
- queryset=VLAN.objects.all(),
- required=False,
- label='Tagged VLANs',
- query_params={
- 'group_id': '$vlan_group',
- 'available_on_device': '$device',
- }
- )
- vrf = DynamicModelChoiceField(
- queryset=VRF.objects.all(),
- required=False,
- label='VRF'
- )
- fieldsets = (
- ('Interface', ('device', 'module', 'name', 'type', 'speed', 'duplex', 'label', 'description', 'tags')),
- ('Addressing', ('vrf', 'mac_address', 'wwn')),
- ('Operation', ('mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected')),
- ('Related Interfaces', ('parent', 'bridge', 'lag')),
- ('802.1Q Switching', ('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans')),
- ('Wireless', (
- 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans',
- )),
- )
- class Meta:
- model = Interface
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag',
- 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel',
- 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans',
- 'vrf', 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- 'speed': SelectSpeedWidget(),
- 'duplex': StaticSelect(),
- 'mode': StaticSelect(),
- 'rf_role': StaticSelect(),
- 'rf_channel': StaticSelect(),
- }
- labels = {
- 'mode': '802.1Q Mode',
- }
- help_texts = {
- 'mode': INTERFACE_MODE_HELP_TEXT,
- 'rf_channel_frequency': "Populated by selected channel (if set)",
- 'rf_channel_width': "Populated by selected channel (if set)",
- }
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- # Restrict LAG/bridge interface assignment by device/VC
- device_id = self.data['device'] if self.is_bound else self.initial.get('device')
- device = Device.objects.filter(pk=device_id).first()
- if device and device.virtual_chassis and device.virtual_chassis.master:
- self.fields['lag'].widget.add_query_param('device_id', device.virtual_chassis.master.pk)
- self.fields['bridge'].widget.add_query_param('device_id', device.virtual_chassis.master.pk)
- class FrontPortForm(NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- rear_port = DynamicModelChoiceField(
- queryset=RearPort.objects.all(),
- query_params={
- 'device_id': '$device',
- }
- )
- class Meta:
- model = FrontPort
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected',
- 'description', 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class RearPortForm(NetBoxModelForm):
- module = DynamicModelChoiceField(
- queryset=Module.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device',
- }
- )
- class Meta:
- model = RearPort
- fields = [
- 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- 'type': StaticSelect(),
- }
- class ModuleBayForm(NetBoxModelForm):
- class Meta:
- model = ModuleBay
- fields = [
- 'device', 'name', 'label', 'position', 'description', 'tags',
- ]
- widgets = {
- 'device': forms.HiddenInput(),
- }
- class DeviceBayForm(NetBoxModelForm):
- 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(NetBoxModelForm):
- device = DynamicModelChoiceField(
- queryset=Device.objects.all()
- )
- parent = DynamicModelChoiceField(
- queryset=InventoryItem.objects.all(),
- required=False,
- query_params={
- 'device_id': '$device'
- }
- )
- role = DynamicModelChoiceField(
- queryset=InventoryItemRole.objects.all(),
- required=False
- )
- manufacturer = DynamicModelChoiceField(
- queryset=Manufacturer.objects.all(),
- required=False
- )
- component_type = ContentTypeChoiceField(
- queryset=ContentType.objects.all(),
- limit_choices_to=MODULAR_COMPONENT_MODELS,
- required=False,
- widget=forms.HiddenInput
- )
- component_id = forms.IntegerField(
- required=False,
- widget=forms.HiddenInput
- )
- fieldsets = (
- ('Inventory Item', ('device', 'parent', 'name', 'label', 'role', 'description', 'tags')),
- ('Hardware', ('manufacturer', 'part_id', 'serial', 'asset_tag')),
- )
- class Meta:
- model = InventoryItem
- fields = [
- 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
- 'description', 'component_type', 'component_id', 'tags',
- ]
- #
- # Device component roles
- #
- class InventoryItemRoleForm(NetBoxModelForm):
- slug = SlugField()
- class Meta:
- model = InventoryItemRole
- fields = [
- 'name', 'slug', 'color', 'description', 'tags',
- ]
|