class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm): site = forms.ModelChoiceField( queryset=Site.objects.all(), required=False, label='Site', widget=forms.Select(attrs={ 'filter-for': 'vlan_group', 'nullable': 'true' })) vlan_group = ChainedModelChoiceField( queryset=VLANGroup.objects.all(), chains=(('site', 'site'), ), required=False, label='VLAN group', widget=APISelect(api_url='/api/ipam/vlan-groups/?site_id={{site}}', attrs={ 'filter-for': 'vlan', 'nullable': 'true' })) vlan = ChainedModelChoiceField( queryset=VLAN.objects.all(), chains=( ('site', 'site'), ('group', 'vlan_group'), ), required=False, label='VLAN', widget=APISelect( api_url='/api/ipam/vlans/?site_id={{site}}&group_id={{vlan_group}}', display_field='display_name')) tags = TagField(required=False) class Meta: model = Prefix fields = [ 'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant', 'tags', ] 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(PrefixForm, self).__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global'
class ClusterAddDevicesForm(BootstrapMixin, ChainedFieldsMixin, forms.Form): region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False, widget=forms.Select(attrs={ 'filter-for': 'site', 'nullable': 'true' })) site = ChainedModelChoiceField( queryset=Site.objects.all(), chains=(('region', 'region'), ), required=False, widget=APISelect(api_url='/api/dcim/sites/?region_id={{region}}', attrs={'filter-for': 'rack'})) rack = ChainedModelChoiceField( queryset=Rack.objects.all(), chains=(('site', 'site'), ), required=False, widget=APISelect(api_url='/api/dcim/racks/?site_id={{site}}', attrs={ 'filter-for': 'devices', 'nullable': 'true' })) devices = ChainedModelMultipleChoiceField( queryset=Device.objects.filter(cluster__isnull=True), chains=( ('site', 'site'), ('rack', 'rack'), ), widget=APISelectMultiple( api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}', display_field='display_name', disabled_indicator='cluster')) class Meta: fields = ['region', 'site', 'rack', 'devices'] def __init__(self, cluster, *args, **kwargs): self.cluster = cluster super(ClusterAddDevicesForm, self).__init__(*args, **kwargs) self.fields['devices'].choices = [] def clean(self): super(ClusterAddDevicesForm, self).clean() # If the Cluster is assigned to a Site, all Devices must be assigned to that Site. if self.cluster.site is not None: for device in self.cleaned_data.get('devices', []): if device.site != self.cluster.site: raise ValidationError({ 'devices': "{} belongs to a different site ({}) than the cluster ({})" .format(device, device.site, self.cluster.site) })
class VLANForm(BootstrapMixin, TenancyForm, CustomFieldForm): site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, widget=forms.Select(attrs={ 'filter-for': 'group', 'nullable': 'true' })) group = ChainedModelChoiceField( queryset=VLANGroup.objects.all(), chains=(('site', 'site'), ), required=False, label='Group', widget=APISelect(api_url='/api/ipam/vlan-groups/?site_id={{site}}', )) class Meta: model = VLAN fields = [ 'site', 'group', 'vid', 'name', 'status', 'role', 'description', 'tenant_group', 'tenant' ] 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", }
class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm): cluster_group = forms.ModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=forms.Select( attrs={'filter-for': 'cluster', 'nullable': 'true'} ) ) cluster = ChainedModelChoiceField( queryset=Cluster.objects.all(), chains=( ('group', 'cluster_group'), ), widget=APISelect( api_url='/api/virtualization/clusters/?group_id={{cluster_group}}' ) ) class Meta: model = VirtualMachine fields = [ 'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments', ] 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(VirtualMachineForm, self).__init__(*args, **kwargs)
class TenancyForm(ChainedFieldsMixin, forms.Form): tenant_group = forms.ModelChoiceField( queryset=TenantGroup.objects.all(), required=False, widget=forms.Select( attrs={'filter-for': 'tenant', 'nullable': 'true'} ) ) tenant = ChainedModelChoiceField( queryset=Tenant.objects.all(), chains=( ('group', 'tenant_group'), ), required=False, widget=APISelect( api_url='/api/tenancy/tenants/?group_id={{tenant_group}}' ) ) def __init__(self, *args, **kwargs): # Initialize helper selector instance = kwargs.get('instance') if instance and instance.tenant is not None: initial = kwargs.get('initial', {}).copy() initial['tenant_group'] = instance.tenant.group kwargs['initial'] = initial super(TenancyForm, self).__init__(*args, **kwargs)
class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm): site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, label='Site', widget=forms.Select(attrs={ 'filter-for': 'vlan', 'nullable': 'true' })) vlan = ChainedModelChoiceField( queryset=VLAN.objects.all(), chains={'site': 'site'}, required=False, label='VLAN', widget=APISelect(api_url='/api/ipam/vlans/?site_id={{site}}', display_field='display_name')) class Meta: model = Prefix fields = [ 'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant' ] def __init__(self, *args, **kwargs): super(PrefixForm, self).__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global'
class VRFForm(BootstrapMixin, CustomFieldForm): tags = TagField(required=False) tenant_group = forms.ModelChoiceField( queryset=TenantGroup.objects.all(), required=False, widget=forms.Select( attrs={'filter-for': 'tenant', 'nullable': 'true'} ) ) tenant = ChainedModelChoiceField( queryset=Tenant.objects.all(), chains=( ('group', 'tenant_group'), ), required=False, widget=APISelect( api_url='/api/tenancy/tenants/?group_id={{tenant_group}}', attrs={'filter-for': 'device', 'nullable': 'true'}, ) ) device = ChainedModelChoiceField( queryset=Device.objects.all(), chains=( ('tenant', 'tenant'), ), required=True, widget=APISelect( api_url='/api/dcim/devices/?tenant_id={{tenant}}' ) ) class Meta: model = VRF fields = ['name', 'rd', 'enforce_unique', 'description', 'tenant_group', 'tenant', 'device', 'tags'] labels = { 'rd': "RD", } help_texts = { 'rd': "Route distinguisher in any format", 'device': "Device on which the VRF is configured" }
class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm): interface = forms.ModelChoiceField(queryset=Interface.objects.all(), required=False) nat_site = forms.ModelChoiceField( queryset=Site.objects.all(), required=False, label='Site', widget=forms.Select(attrs={'filter-for': 'nat_rack'})) nat_rack = ChainedModelChoiceField( queryset=Rack.objects.all(), chains=(('site', 'nat_site'), ), required=False, label='Rack', widget=APISelect(api_url='/api/dcim/racks/?site_id={{nat_site}}', display_field='display_name', attrs={ 'filter-for': 'nat_device', 'nullable': 'true' })) nat_device = ChainedModelChoiceField( queryset=Device.objects.all(), chains=( ('site', 'nat_site'), ('rack', 'nat_rack'), ), required=False, label='Device', widget=APISelect( api_url= '/api/dcim/devices/?site_id={{nat_site}}&rack_id={{nat_rack}}', display_field='display_name', attrs={'filter-for': 'nat_inside'})) nat_inside = ChainedModelChoiceField( queryset=IPAddress.objects.all(), chains=(('interface__device', 'nat_device'), ), required=False, label='IP Address', widget=APISelect( api_url='/api/ipam/ip-addresses/?device_id={{nat_device}}', display_field='address')) livesearch = forms.CharField(required=False, label='Search', widget=Livesearch( query_key='q', query_url='ipam-api:ipaddress-list', field_to_update='nat_inside', obj_label='address')) primary_for_parent = forms.BooleanField( required=False, label='Make this the primary IP for the device/VM') class Meta: model = IPAddress fields = [ 'address', 'vrf', 'status', 'role', 'description', 'interface', 'primary_for_parent', 'nat_site', 'nat_rack', 'nat_inside', 'tenant_group', 'tenant', ] 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(IPAddressForm, self).__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) 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(IPAddressForm, self).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(IPAddressForm, self).save(*args, **kwargs) # Assign this IPAddress as the primary for the associated Device. 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() # Clear assignment as primary for device if set. else: try: if ipaddress.address.version == 4: device = ipaddress.primary_ip4_for device.primary_ip4 = None else: device = ipaddress.primary_ip6_for device.primary_ip6 = None device.save() except Device.DoesNotExist: pass return ipaddress
class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm): interface = forms.ModelChoiceField(queryset=Interface.objects.all(), required=False) nat_site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, label='Site', widget=APISelect( api_url="/api/dcim/sites/", filter_for={ 'nat_rack': 'site_id', 'nat_device': 'site_id' })) nat_rack = ChainedModelChoiceField( queryset=Rack.objects.all(), chains=(('site', 'nat_site'), ), required=False, label='Rack', widget=APISelect(api_url='/api/dcim/racks/', display_field='display_name', filter_for={'nat_device': 'rack_id'}, attrs={'nullable': 'true'})) nat_device = ChainedModelChoiceField( queryset=Device.objects.all(), chains=( ('site', 'nat_site'), ('rack', 'nat_rack'), ), required=False, label='Device', widget=APISelect(api_url='/api/dcim/devices/', display_field='display_name', filter_for={'nat_inside': 'device_id'})) nat_vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF', widget=APISelect( api_url="/api/ipam/vrfs/", filter_for={'nat_inside': 'vrf_id'})) nat_inside = ChainedModelChoiceField( queryset=IPAddress.objects.all(), chains=(('interface__device', 'nat_device'), ), required=False, label='IP Address', widget=APISelect(api_url='/api/ipam/ip-addresses/', 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(), 'vrf': APISelect(api_url="/api/ipam/vrfs/") } 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) 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 VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm): cluster_group = forms.ModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=forms.Select(attrs={ 'filter-for': 'cluster', 'nullable': 'true' })) cluster = ChainedModelChoiceField( queryset=Cluster.objects.all(), chains=(('group', 'cluster_group'), ), widget=APISelect( api_url='/api/virtualization/clusters/?group_id={{cluster_group}}') ) class Meta: model = VirtualMachine fields = [ 'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', ] 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(VirtualMachineForm, self).__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.select_related( 'interface').filter( 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.select_related( 'nat_inside').filter( 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
class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): cluster_group = forms.ModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=APISelect( api_url='/api/virtualization/cluster-groups/', filter_for={ "cluster": "group_id", }, attrs={ 'nullable': 'true', } ) ) cluster = ChainedModelChoiceField( queryset=Cluster.objects.all(), chains=( ('group', 'cluster_group'), ), widget=APISelect( api_url='/api/virtualization/clusters/' ) ) 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(), "role": APISelect( api_url="/api/dcim/device-roles/", additional_query_params={ "vm_role": "True" } ), 'primary_ip4': StaticSelect2(), 'primary_ip6': StaticSelect2(), 'platform': APISelect( api_url='/api/dcim/platforms/' ) } 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( 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( 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
class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm): site = forms.ModelChoiceField( queryset=Site.objects.all(), widget=forms.Select(attrs={'filter-for': 'rack'})) rack = ChainedModelChoiceField( queryset=Rack.objects.all(), chains=(('site', 'site'), ), required=False, label='Rack', widget=APISelect(api_url='/api/dcim/racks/?site_id={{site}}', attrs={ 'filter-for': 'device', 'nullable': 'true' })) device = ChainedModelChoiceField( queryset=Device.objects.all(), chains=( ('site', 'site'), ('rack', 'rack'), ), required=False, label='Device', widget=APISelect( api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}', display_field='display_name', attrs={'filter-for': 'interface'})) interface = ChainedModelChoiceField( queryset=Interface.objects.exclude( form_factor__in=VIRTUAL_IFACE_TYPES).select_related( 'circuit_termination', 'connected_as_a', 'connected_as_b'), chains=(('device', 'device'), ), required=False, label='Interface', widget=APISelect( api_url='/api/dcim/interfaces/?device_id={{device}}&type=physical', disabled_indicator='connection')) class Meta: model = CircuitTermination fields = [ 'term_side', 'site', 'rack', 'device', 'interface', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', ] help_texts = { 'port_speed': "Physical circuit speed", 'xconnect_id': "ID of the local cross-connect", 'pp_info': "Patch panel ID and port number(s)" } widgets = { 'term_side': forms.HiddenInput(), } def __init__(self, *args, **kwargs): # Initialize helper selectors instance = kwargs.get('instance') if instance and instance.interface is not None: initial = kwargs.get('initial', {}) initial['rack'] = instance.interface.device.rack initial['device'] = instance.interface.device kwargs['initial'] = initial super(CircuitTerminationForm, self).__init__(*args, **kwargs) # Mark connected interfaces as disabled self.fields['interface'].choices = [] for iface in self.fields['interface'].queryset: self.fields['interface'].choices.append((iface.id, { 'label': iface.name, 'disabled': iface.is_connected and iface.pk != self.initial.get('interface'), }))