1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

IPAM Select2 forms and changelog

This commit is contained in:
John Anderson
2019-01-10 21:19:13 -05:00
parent 5f1f8ee73b
commit ad4fb3ce8b
6 changed files with 268 additions and 140 deletions

View File

@ -1,7 +1,6 @@
from django import forms
from django.core.exceptions import MultipleObjectsReturned
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db.models import Count
from taggit.forms import TagField
from dcim.models import Site, Rack, Device, Interface
@ -9,9 +8,9 @@ from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEdit
from tenancy.forms import TenancyForm
from tenancy.models import Tenant
from utilities.forms import (
AnnotatedMultipleChoiceField, APISelect, BootstrapMixin, BulkEditNullBooleanSelect, ChainedModelChoiceField,
CSVChoiceField, ExpandableIPAddressField, FilterChoiceField, FlexibleModelChoiceField, Livesearch, ReturnURLForm,
SlugField, add_blank_choice,
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditNullBooleanSelect, ChainedModelChoiceField,
CSVChoiceField, ExpandableIPAddressField, FilterChoiceField, FlexibleModelChoiceField, ReturnURLForm, SlugField,
StaticSelect2, StaticSelect2Multiple, BOOLEAN_WITH_BLANK_CHOICES
)
from virtualization.models import VirtualMachine
from .constants import (
@ -77,7 +76,10 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm
)
tenant = forms.ModelChoiceField(
queryset=Tenant.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/tenancy/tenants/"
)
)
enforce_unique = forms.NullBooleanField(
required=False,
@ -102,11 +104,14 @@ class VRFFilterForm(BootstrapMixin, CustomFieldFilterForm):
label='Search'
)
tenant = FilterChoiceField(
queryset=Tenant.objects.annotate(
filter_count=Count('vrfs')
),
queryset=Tenant.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/tenancy/tenants/",
value_field="slug",
null_option=True,
)
)
@ -139,12 +144,8 @@ class RIRFilterForm(BootstrapMixin, forms.Form):
is_private = forms.NullBooleanField(
required=False,
label='Private',
widget=forms.Select(
choices=[
('', '---------'),
('True', 'Yes'),
('False', 'No'),
]
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
@ -168,6 +169,11 @@ class AggregateForm(BootstrapMixin, CustomFieldForm):
'rir': "Regional Internet Registry responsible for this prefix",
'date_added': "Format: YYYY-MM-DD",
}
widgets = {
'rir': APISelect(
api_url="/api/ipam/rirs/"
)
}
class AggregateCSVForm(forms.ModelForm):
@ -193,7 +199,10 @@ class AggregateBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
rir = forms.ModelChoiceField(
queryset=RIR.objects.all(),
required=False,
label='RIR'
label='RIR',
widget=APISelect(
api_url="/api/ipam/rirs/"
)
)
date_added = forms.DateField(
required=False
@ -218,12 +227,17 @@ class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm):
family = forms.ChoiceField(
required=False,
choices=IP_FAMILY_CHOICES,
label='Address family'
label='Address family',
widget=StaticSelect2()
)
rir = FilterChoiceField(
queryset=RIR.objects.annotate(filter_count=Count('aggregates')),
queryset=RIR.objects.all(),
to_field_name='slug',
label='RIR'
label='RIR',
widget=APISelectMultiple(
api_url="/api/ipam/rirs/",
value_field="slug",
)
)
@ -261,9 +275,13 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
queryset=Site.objects.all(),
required=False,
label='Site',
widget=forms.Select(
widget=APISelect(
api_url="/api/dcim/sites/",
filter_for={
'vlan_group': 'site_id',
'vlan': 'site_id',
},
attrs={
'filter-for': 'vlan_group',
'nullable': 'true',
}
)
@ -276,9 +294,11 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
required=False,
label='VLAN group',
widget=APISelect(
api_url='/api/ipam/vlan-groups/?site_id={{site}}',
api_url='/api/ipam/vlan-groups/',
filter_for={
'vlan': 'group_id'
},
attrs={
'filter-for': 'vlan',
'nullable': 'true',
}
)
@ -292,7 +312,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
required=False,
label='VLAN',
widget=APISelect(
api_url='/api/ipam/vlans/?site_id={{site}}&group_id={{vlan_group}}',
api_url='/api/ipam/vlans/',
display_field='display_name'
)
)
@ -304,6 +324,15 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant',
'tags',
]
widgets = {
'vrf': APISelect(
api_url="/api/ipam/vrfs/"
),
'status': StaticSelect2(),
'role': APISelect(
api_url="/api/ipam/roles/"
)
}
def __init__(self, *args, **kwargs):
@ -415,12 +444,18 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
)
site = forms.ModelChoiceField(
queryset=Site.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/dcim/sites/"
)
)
vrf = forms.ModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label='VRF',
widget=APISelect(
api_url="/api/ipam/vrfs/"
)
)
prefix_length = forms.IntegerField(
min_value=1,
@ -429,15 +464,22 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
)
tenant = forms.ModelChoiceField(
queryset=Tenant.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/tenancy/tenants/"
)
)
status = forms.ChoiceField(
choices=add_blank_choice(PREFIX_STATUS_CHOICES),
required=False
required=False,
widget=StaticSelect2()
)
role = forms.ModelChoiceField(
queryset=Role.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/ipam/roles/"
)
)
is_pool = forms.NullBooleanField(
required=False,
@ -473,47 +515,60 @@ class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
family = forms.ChoiceField(
required=False,
choices=IP_FAMILY_CHOICES,
label='Address family'
label='Address family',
widget=StaticSelect2()
)
mask_length = forms.ChoiceField(
required=False,
choices=PREFIX_MASK_LENGTH_CHOICES,
label='Mask length'
label='Mask length',
widget=StaticSelect2()
)
vrf = FilterChoiceField(
queryset=VRF.objects.annotate(
filter_count=Count('prefixes')
),
queryset=VRF.objects.all(),
to_field_name='rd',
label='VRF',
null_label='-- Global --'
null_label='-- Global --',
widget=APISelectMultiple(
api_url="/api/ipam/vrfs/",
value_field="slug",
null_option=True,
)
)
tenant = FilterChoiceField(
queryset=Tenant.objects.annotate(
filter_count=Count('prefixes')
),
queryset=Tenant.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/tenancy/tenants/",
value_field="slug",
null_option=True,
)
)
status = AnnotatedMultipleChoiceField(
status = forms.MultipleChoiceField(
choices=PREFIX_STATUS_CHOICES,
annotate=Prefix.objects.all(),
annotate_field='status',
required=False
required=False,
widget=StaticSelect2Multiple()
)
site = FilterChoiceField(
queryset=Site.objects.annotate(
filter_count=Count('prefixes')
),
queryset=Site.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field="slug",
null_option=True,
)
)
role = FilterChoiceField(
queryset=Role.objects.annotate(
filter_count=Count('prefixes')
),
queryset=Role.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/ipam/roles/",
value_field="slug",
null_option=True,
)
)
expand = forms.BooleanField(
required=False,
@ -534,9 +589,11 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
queryset=Site.objects.all(),
required=False,
label='Site',
widget=forms.Select(
attrs={
'filter-for': 'nat_rack'
widget=APISelect(
api_url="/api/dcim/sites/",
filter_for={
'nat_rack': 'site_id',
'nat_device': 'site_id'
}
)
)
@ -548,10 +605,12 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
required=False,
label='Rack',
widget=APISelect(
api_url='/api/dcim/racks/?site_id={{nat_site}}',
api_url='/api/dcim/racks/',
display_field='display_name',
filter_for={
'nat_device': 'rack_id'
},
attrs={
'filter-for': 'nat_device',
'nullable': 'true'
}
)
@ -565,9 +624,11 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
required=False,
label='Device',
widget=APISelect(
api_url='/api/dcim/devices/?site_id={{nat_site}}&rack_id={{nat_rack}}',
api_url='/api/dcim/devices/',
display_field='display_name',
attrs={'filter-for': 'nat_inside'}
filter_for={
'nat_inside': 'device_id'
}
)
)
nat_inside = ChainedModelChoiceField(
@ -578,20 +639,10 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
required=False,
label='IP Address',
widget=APISelect(
api_url='/api/ipam/ip-addresses/?device_id={{nat_device}}',
api_url='/api/ipam/ip-addresses/',
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'
@ -606,6 +657,13 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
'address', 'vrf', 'status', 'role', '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):
@ -685,6 +743,13 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldForm):
fields = [
'address', 'vrf', 'status', 'role', 'description', 'tenant_group', 'tenant',
]
widgets = {
'status': StaticSelect2(),
'role': StaticSelect2(),
'vrf': APISelect(
api_url="/api/ipam/vrfs/"
)
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -822,7 +887,10 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
vrf = forms.ModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label='VRF',
widget=APISelect(
api_url="/api/ipam/vrfs/"
)
)
mask_length = forms.IntegerField(
min_value=1,
@ -831,15 +899,20 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
)
tenant = forms.ModelChoiceField(
queryset=Tenant.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/tenancy/tenants/"
)
)
status = forms.ChoiceField(
choices=add_blank_choice(IPADDRESS_STATUS_CHOICES),
required=False
required=False,
widget=StaticSelect2()
)
role = forms.ChoiceField(
choices=add_blank_choice(IPADDRESS_ROLE_CHOICES),
required=False
required=False,
widget=StaticSelect2()
)
description = forms.CharField(
max_length=100, required=False
@ -856,7 +929,10 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form):
queryset=VRF.objects.all(),
required=False,
label='VRF',
empty_label='Global'
empty_label='Global',
widget=APISelect(
api_url="/api/ipam/vrfs/"
)
)
address = forms.CharField(
label='IP Address'
@ -881,39 +957,45 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
family = forms.ChoiceField(
required=False,
choices=IP_FAMILY_CHOICES,
label='Address family'
label='Address family',
widget=StaticSelect2()
)
mask_length = forms.ChoiceField(
required=False,
choices=IPADDRESS_MASK_LENGTH_CHOICES,
label='Mask length'
label='Mask length',
widget=StaticSelect2()
)
vrf = FilterChoiceField(
queryset=VRF.objects.annotate(
filter_count=Count('ip_addresses')
),
queryset=VRF.objects.all(),
to_field_name='rd',
label='VRF',
null_label='-- Global --'
null_label='-- Global --',
widget=APISelectMultiple(
api_url="/api/ipam/vrfs/",
value_field="slug",
null_option=True,
)
)
tenant = FilterChoiceField(
queryset=Tenant.objects.annotate(
filter_count=Count('ip_addresses')
),
queryset=Tenant.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/tenancy/tenants/",
value_field="slug",
null_option=True,
)
)
status = AnnotatedMultipleChoiceField(
status = forms.MultipleChoiceField(
choices=IPADDRESS_STATUS_CHOICES,
annotate=IPAddress.objects.all(),
annotate_field='status',
required=False
required=False,
widget=StaticSelect2Multiple()
)
role = AnnotatedMultipleChoiceField(
role = forms.MultipleChoiceField(
choices=IPADDRESS_ROLE_CHOICES,
annotate=IPAddress.objects.all(),
annotate_field='role',
required=False
required=False,
widget=StaticSelect2Multiple()
)
@ -929,6 +1011,11 @@ class VLANGroupForm(BootstrapMixin, forms.ModelForm):
fields = [
'site', 'name', 'slug',
]
widgets = {
'site': APISelect(
api_url="/api/dcim/sites/"
)
}
class VLANGroupCSVForm(forms.ModelForm):
@ -953,11 +1040,14 @@ class VLANGroupCSVForm(forms.ModelForm):
class VLANGroupFilterForm(BootstrapMixin, forms.Form):
site = FilterChoiceField(
queryset=Site.objects.annotate(
filter_count=Count('vlan_groups')
),
queryset=Site.objects.all(),
to_field_name='slug',
null_label='-- Global --'
null_label='-- Global --',
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field="slug",
null_option=True,
)
)
@ -969,9 +1059,12 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldForm):
site = forms.ModelChoiceField(
queryset=Site.objects.all(),
required=False,
widget=forms.Select(
widget=APISelect(
api_url="/api/dcim/sites/",
filter_for={
'group': 'site_id'
},
attrs={
'filter-for': 'group',
'nullable': 'true',
}
)
@ -984,7 +1077,7 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldForm):
required=False,
label='Group',
widget=APISelect(
api_url='/api/ipam/vlan-groups/?site_id={{site}}',
api_url='/api/ipam/vlan-groups/',
)
)
tags = TagField(required=False)
@ -1002,6 +1095,12 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldForm):
'status': "Operational status of this VLAN",
'role': "The primary function of this VLAN",
}
widgets = {
'status': StaticSelect2(),
'role': APISelect(
api_url="/api/ipam/roles/"
)
}
class VLANCSVForm(forms.ModelForm):
@ -1077,23 +1176,36 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
)
site = forms.ModelChoiceField(
queryset=Site.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/dcim/sites/"
)
)
group = forms.ModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/ipam/vlan-groups/"
)
)
tenant = forms.ModelChoiceField(
queryset=Tenant.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/tenancy/tenants/"
)
)
status = forms.ChoiceField(
choices=add_blank_choice(VLAN_STATUS_CHOICES),
required=False
required=False,
widget=StaticSelect2()
)
role = forms.ModelChoiceField(
queryset=Role.objects.all(),
required=False
required=False,
widget=APISelect(
api_url="/api/ipam/roles/"
)
)
description = forms.CharField(
max_length=100,
@ -1113,38 +1225,48 @@ class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm):
label='Search'
)
site = FilterChoiceField(
queryset=Site.objects.annotate(
filter_count=Count('vlans')
),
queryset=Site.objects.all(),
to_field_name='slug',
null_label='-- Global --'
null_label='-- Global --',
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field="slug",
null_option=True,
)
)
group_id = FilterChoiceField(
queryset=VLANGroup.objects.annotate(
filter_count=Count('vlans')
),
queryset=VLANGroup.objects.all(),
label='VLAN group',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/ipam/vlan-groups/",
null_option=True,
)
)
tenant = FilterChoiceField(
queryset=Tenant.objects.annotate(
filter_count=Count('vlans')
),
queryset=Tenant.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/tenancy/tenants/",
value_field="slug",
null_option=True,
)
)
status = AnnotatedMultipleChoiceField(
status = forms.MultipleChoiceField(
choices=VLAN_STATUS_CHOICES,
annotate=VLAN.objects.all(),
annotate_field='status',
required=False
required=False,
widget=StaticSelect2Multiple()
)
role = FilterChoiceField(
queryset=Role.objects.annotate(
filter_count=Count('vlans')
),
queryset=Role.objects.all(),
to_field_name='slug',
null_label='-- None --'
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/ipam/roles/",
value_field="slug",
null_option=True,
)
)
@ -1166,6 +1288,10 @@ class ServiceForm(BootstrapMixin, CustomFieldForm):
'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)
@ -1192,10 +1318,11 @@ class ServiceFilterForm(BootstrapMixin, CustomFieldFilterForm):
)
protocol = forms.ChoiceField(
choices=add_blank_choice(IP_PROTOCOL_CHOICES),
required=False
required=False,
widget=StaticSelect2Multiple()
)
port = forms.IntegerField(
required=False
required=False,
)
@ -1206,7 +1333,8 @@ class ServiceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
)
protocol = forms.ChoiceField(
choices=add_blank_choice(IP_PROTOCOL_CHOICES),
required=False
required=False,
widget=StaticSelect2()
)
port = forms.IntegerField(
validators=[
@ -1222,5 +1350,5 @@ class ServiceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
class Meta:
nullable_fields = [
'site', 'group', 'tenant', 'role', 'description',
'site', 'tenant', 'role', 'description',
]