class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): vrf = DynamicModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF') site = DynamicModelChoiceField(queryset=Site.objects.all(), required=False, widget=APISelect(filter_for={ 'vlan_group': 'site_id', 'vlan': 'site_id', }, attrs={ 'nullable': 'true', })) vlan_group = DynamicModelChoiceField(queryset=VLANGroup.objects.all(), required=False, label='VLAN group', widget=APISelect( filter_for={'vlan': 'group_id'}, attrs={ 'nullable': 'true', })) vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, label='VLAN', widget=APISelect(display_field='display_name')) role = DynamicModelChoiceField(queryset=Role.objects.all(), required=False) tags = TagField(required=False) class Meta: model = Prefix fields = [ 'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant', 'tags', ] widgets = { 'status': StaticSelect2(), } def __init__(self, *args, **kwargs): # Initialize helper selectors instance = kwargs.get('instance') initial = kwargs.get('initial', {}).copy() if instance and instance.vlan is not None: initial['vlan_group'] = instance.vlan.group kwargs['initial'] = initial super().__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global'
class ProviderForm(BootstrapMixin, CustomFieldModelForm): slug = SlugField() comments = CommentField() tags = TagField(required=False) class Meta: model = Provider fields = [ 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags', ] widgets = { 'noc_contact': SmallTextarea(attrs={'rows': 5}), 'admin_contact': SmallTextarea(attrs={'rows': 5}), } help_texts = { 'name': "Full name of the provider", 'asn': "BGP autonomous system number (if applicable)", 'portal_url': "URL of the provider's customer support portal", 'noc_contact': "NOC email address and phone number", 'admin_contact': "Administrative contact email address and phone number", }
class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): vrf = DynamicModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF') tags = TagField(required=False) class Meta: model = IPAddress fields = [ 'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'tenant_group', 'tenant', 'tags', ] widgets = { 'status': StaticSelect2(), 'role': StaticSelect2(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global'
class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): provider = DynamicModelChoiceField(queryset=Provider.objects.all()) type = DynamicModelChoiceField(queryset=CircuitType.objects.all()) comments = CommentField() tags = TagField(required=False) class Meta: model = Circuit fields = [ 'cid', 'type', 'provider', 'status', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant', 'comments', 'tags', ] help_texts = { 'cid': "Unique circuit ID", 'commit_rate': "Committed rate", } widgets = { 'status': StaticSelect2(), 'install_date': DatePicker(), }
class SecretForm(BootstrapMixin, CustomFieldModelForm): device = DynamicModelChoiceField( queryset=Device.objects.all() ) plaintext = forms.CharField( max_length=SECRET_PLAINTEXT_MAX_LENGTH, required=False, label='Plaintext', widget=forms.PasswordInput( attrs={ 'class': 'requires-session-key', } ) ) plaintext2 = forms.CharField( max_length=SECRET_PLAINTEXT_MAX_LENGTH, required=False, label='Plaintext (verify)', widget=forms.PasswordInput() ) role = DynamicModelChoiceField( queryset=SecretRole.objects.all() ) tags = TagField( required=False ) class Meta: model = Secret fields = [ 'device', 'role', 'name', 'plaintext', 'plaintext2', 'tags', ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # A plaintext value is required when creating a new Secret if not self.instance.pk: self.fields['plaintext'].required = True def clean(self): # Verify that the provided plaintext values match if self.cleaned_data['plaintext'] != self.cleaned_data['plaintext2']: raise forms.ValidationError({ 'plaintext2': "The two given plaintext values do not match. Please check your input." }) # Validate uniqueness if Secret.objects.filter( device=self.cleaned_data['device'], role=self.cleaned_data['role'], name=self.cleaned_data['name'] ).exists(): raise forms.ValidationError( "Each secret assigned to a device must have a unique combination of role and name" )
class InterfaceCreateForm(BootstrapMixin, forms.Form): virtual_machine = forms.ModelChoiceField( queryset=VirtualMachine.objects.all(), widget=forms.HiddenInput()) name_pattern = ExpandableNameField(label='Name') type = forms.ChoiceField(choices=VMInterfaceTypeChoices, initial=VMInterfaceTypeChoices.TYPE_VIRTUAL, widget=forms.HiddenInput()) enabled = forms.BooleanField(required=False, initial=True) mtu = forms.IntegerField(required=False, min_value=INTERFACE_MTU_MIN, max_value=INTERFACE_MTU_MAX, label='MTU') mac_address = forms.CharField(required=False, label='MAC Address') description = forms.CharField(max_length=100, required=False) mode = forms.ChoiceField( choices=add_blank_choice(InterfaceModeChoices), required=False, widget=StaticSelect2(), ) untagged_vlan = DynamicModelChoiceField(queryset=VLAN.objects.all(), required=False, widget=APISelect( display_field='display_name', full=True, additional_query_params={ 'site_id': 'null', }, )) tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( display_field='display_name', full=True, additional_query_params={ 'site_id': 'null', }, )) tags = TagField(required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) virtual_machine = VirtualMachine.objects.get( pk=self.initial.get('virtual_machine') or self.data.get('virtual_machine')) site = getattr(virtual_machine.cluster, 'site', None) if site is not None: # Add current site to VLANs query params self.fields['untagged_vlan'].widget.add_additional_query_param( 'site_id', site.pk) self.fields['tagged_vlans'].widget.add_additional_query_param( 'site_id', site.pk)
class TenantForm(BootstrapMixin, CustomFieldModelForm): slug = SlugField() group = DynamicModelChoiceField(queryset=TenantGroup.objects.all(), required=False) comments = CommentField() tags = TagField(required=False) class Meta: model = Tenant fields = ( 'name', 'slug', 'group', 'description', 'comments', 'tags', )
class ClusterForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): type = DynamicModelChoiceField(queryset=ClusterType.objects.all()) group = DynamicModelChoiceField(queryset=ClusterGroup.objects.all(), required=False) site = DynamicModelChoiceField(queryset=Site.objects.all(), required=False) comments = CommentField() tags = TagField(required=False) class Meta: model = Cluster fields = ( 'name', 'type', 'group', 'tenant', 'site', 'comments', 'tags', )
class VRFForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): tags = TagField(required=False) class Meta: model = VRF fields = [ 'name', 'rd', 'enforce_unique', 'description', 'tenant_group', 'tenant', 'tags', ] labels = { 'rd': "RD", } help_texts = { 'rd': "Route distinguisher in any format", }
class AggregateForm(BootstrapMixin, CustomFieldModelForm): rir = DynamicModelChoiceField(queryset=RIR.objects.all()) tags = TagField(required=False) class Meta: model = Aggregate fields = [ 'prefix', 'rir', 'date_added', 'description', 'tags', ] help_texts = { 'prefix': "IPv4 or IPv6 network", 'rir': "Regional Internet Registry responsible for this prefix", } widgets = { 'date_added': DatePicker(), }
class ServiceForm(BootstrapMixin, CustomFieldModelForm): port = forms.IntegerField(min_value=SERVICE_PORT_MIN, max_value=SERVICE_PORT_MAX) tags = TagField(required=False) class Meta: model = Service fields = [ 'name', 'protocol', 'port', 'ipaddresses', 'description', 'tags', ] help_texts = { 'ipaddresses': "IP address assignment is optional. If no IPs are selected, the service is assumed to be " "reachable via all IPs assigned to the device.", } widgets = { 'protocol': StaticSelect2(), 'ipaddresses': StaticSelect2Multiple(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit IP address choices to those assigned to interfaces of the parent device/VM if self.instance.device: vc_interface_ids = [ i['id'] for i in self.instance.device.vc_interfaces.values('id') ] self.fields['ipaddresses'].queryset = IPAddress.objects.filter( interface_id__in=vc_interface_ids) elif self.instance.virtual_machine: self.fields['ipaddresses'].queryset = IPAddress.objects.filter( interface__virtual_machine=self.instance.virtual_machine) else: self.fields['ipaddresses'].choices = []
class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): site = DynamicModelChoiceField(queryset=Site.objects.all(), required=False, widget=APISelect( filter_for={'group': 'site_id'}, attrs={ 'nullable': 'true', })) group = DynamicModelChoiceField(queryset=VLANGroup.objects.all(), required=False) role = DynamicModelChoiceField(queryset=Role.objects.all(), required=False) tags = TagField(required=False) class Meta: model = VLAN fields = [ 'site', 'group', 'vid', 'name', 'status', 'role', 'description', 'tenant_group', 'tenant', 'tags', ] help_texts = { 'site': "Leave blank if this VLAN spans multiple sites", 'group': "VLAN group (optional)", 'vid': "Configured VLAN ID", 'name': "Configured VLAN name", 'status': "Operational status of this VLAN", 'role': "The primary function of this VLAN", } widgets = { 'status': StaticSelect2(), }
class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModelForm): interface = forms.ModelChoiceField(queryset=Interface.objects.all(), required=False) vrf = DynamicModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF') nat_site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, label='Site', widget=APISelect(filter_for={ 'nat_rack': 'site_id', 'nat_device': 'site_id' })) nat_rack = DynamicModelChoiceField( queryset=Rack.objects.all(), required=False, label='Rack', widget=APISelect(display_field='display_name', filter_for={'nat_device': 'rack_id'}, attrs={'nullable': 'true'})) nat_device = DynamicModelChoiceField( queryset=Device.objects.all(), required=False, label='Device', widget=APISelect(display_field='display_name', filter_for={'nat_inside': 'device_id'})) nat_vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label='VRF', widget=APISelect(filter_for={'nat_inside': 'vrf_id'})) nat_inside = DynamicModelChoiceField( queryset=IPAddress.objects.all(), required=False, label='IP Address', widget=APISelect(display_field='address')) primary_for_parent = forms.BooleanField( required=False, label='Make this the primary IP for the device/VM') tags = TagField(required=False) class Meta: model = IPAddress fields = [ 'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'interface', 'primary_for_parent', 'nat_site', 'nat_rack', 'nat_inside', 'tenant_group', 'tenant', 'tags', ] widgets = { 'status': StaticSelect2(), 'role': StaticSelect2(), } def __init__(self, *args, **kwargs): # Initialize helper selectors instance = kwargs.get('instance') initial = kwargs.get('initial', {}).copy() if instance and instance.nat_inside and instance.nat_inside.device is not None: initial['nat_site'] = instance.nat_inside.device.site initial['nat_rack'] = instance.nat_inside.device.rack initial['nat_device'] = instance.nat_inside.device kwargs['initial'] = initial super().__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global' # Limit interface selections to those belonging to the parent device/VM if self.instance and self.instance.interface: self.fields['interface'].queryset = Interface.objects.filter( device=self.instance.interface.device, virtual_machine=self.instance.interface.virtual_machine ).prefetch_related( 'device__primary_ip4', 'device__primary_ip6', 'virtual_machine__primary_ip4', 'virtual_machine__primary_ip6', ) # We prefetch the primary address fields to ensure cache invalidation does not balk on the save() else: self.fields['interface'].choices = [] # Initialize primary_for_parent if IP address is already assigned if self.instance.pk and self.instance.interface is not None: parent = self.instance.interface.parent if (self.instance.address.version == 4 and parent.primary_ip4_id == self.instance.pk or self.instance.address.version == 6 and parent.primary_ip6_id == self.instance.pk): self.initial['primary_for_parent'] = True def clean(self): super().clean() # Primary IP assignment is only available if an interface has been assigned. if self.cleaned_data.get('primary_for_parent' ) and not self.cleaned_data.get('interface'): self.add_error( 'primary_for_parent', "Only IP addresses assigned to an interface can be designated as primary IPs." ) def save(self, *args, **kwargs): ipaddress = super().save(*args, **kwargs) # Assign/clear this IPAddress as the primary for the associated Device/VirtualMachine. if self.cleaned_data['primary_for_parent']: parent = self.cleaned_data['interface'].parent if ipaddress.address.version == 4: parent.primary_ip4 = ipaddress else: parent.primary_ip6 = ipaddress parent.save() elif self.cleaned_data['interface']: parent = self.cleaned_data['interface'].parent if ipaddress.address.version == 4 and parent.primary_ip4 == ipaddress: parent.primary_ip4 = None parent.save() elif ipaddress.address.version == 6 and parent.primary_ip6 == ipaddress: parent.primary_ip6 = None parent.save() return ipaddress
class InterfaceForm(BootstrapMixin, forms.ModelForm): untagged_vlan = DynamicModelChoiceField(queryset=VLAN.objects.all(), required=False, widget=APISelect( display_field='display_name', full=True, additional_query_params={ 'site_id': 'null', }, )) tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( display_field='display_name', full=True, additional_query_params={ 'site_id': 'null', }, )) tags = TagField(required=False) class Meta: model = Interface fields = [ 'virtual_machine', 'name', 'type', 'enabled', 'mac_address', 'mtu', 'description', 'mode', 'tags', 'untagged_vlan', 'tagged_vlans', ] widgets = { 'virtual_machine': forms.HiddenInput(), 'type': forms.HiddenInput(), 'mode': StaticSelect2() } labels = { 'mode': '802.1Q Mode', } help_texts = { 'mode': INTERFACE_MODE_HELP_TEXT, } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add current site to VLANs query params site = getattr(self.instance.parent, 'site', None) if site is not None: # Add current site to VLANs query params self.fields['untagged_vlan'].widget.add_additional_query_param( 'site_id', site.pk) self.fields['tagged_vlans'].widget.add_additional_query_param( 'site_id', site.pk) def clean(self): super().clean() # Validate VLAN assignments tagged_vlans = self.cleaned_data['tagged_vlans'] # Untagged interfaces cannot be assigned tagged VLANs if self.cleaned_data[ 'mode'] == InterfaceModeChoices.MODE_ACCESS and 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 VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): cluster_group = DynamicModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=APISelect(filter_for={ "cluster": "group_id", }, attrs={ 'nullable': 'true', })) cluster = DynamicModelChoiceField(queryset=Cluster.objects.all()) role = DynamicModelChoiceField( queryset=DeviceRole.objects.all(), required=False, widget=APISelect(additional_query_params={"vm_role": "True"})) platform = DynamicModelChoiceField(queryset=Platform.objects.all(), required=False) tags = TagField(required=False) local_context_data = JSONField(required=False, label='') class Meta: model = VirtualMachine fields = [ 'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data', ] help_texts = { 'local_context_data': "Local config context data overwrites all sources contexts in the final rendered " "config context", } widgets = { "status": StaticSelect2(), 'primary_ip4': StaticSelect2(), 'primary_ip6': StaticSelect2(), } def __init__(self, *args, **kwargs): # Initialize helper selector instance = kwargs.get('instance') if instance.pk and instance.cluster is not None: initial = kwargs.get('initial', {}).copy() initial['cluster_group'] = instance.cluster.group kwargs['initial'] = initial 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, '---------')] # Collect interface IPs interface_ips = IPAddress.objects.prefetch_related( 'interface').filter( address__family=family, interface__virtual_machine=self.instance) if interface_ips: ip_choices.append(('Interface IPs', [ (ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips ])) # Collect NAT IPs nat_ips = IPAddress.objects.prefetch_related( 'nat_inside').filter( address__family=family, nat_inside__interface__virtual_machine=self.instance) if nat_ips: ip_choices.append(('NAT IPs', [ (ip.id, '{} ({})'.format(ip.address, ip.nat_inside.address)) for ip in nat_ips ])) self.fields['primary_ip{}'.format(family)].choices = ip_choices 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