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

338 lines
11 KiB
Python

from __future__ import unicode_literals
from mptt.forms import TreeNodeChoiceField
from django import forms
from django.db.models import Count
from dcim.constants import IFACE_FF_VIRTUAL, VIFACE_FF_CHOICES
from dcim.formfields import MACAddressFormField
from dcim.models import Device, Interface, Platform, Rack, Region, Site
from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
from tenancy.forms import TenancyForm
from tenancy.models import Tenant
from utilities.forms import (
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
ChainedFieldsMixin, ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm,
ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, SlugField, SmallTextarea,
)
from .constants import STATUS_CHOICES
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
#
# Cluster types
#
class ClusterTypeForm(BootstrapMixin, forms.ModelForm):
slug = SlugField()
class Meta:
model = ClusterType
fields = ['name', 'slug']
#
# Cluster groups
#
class ClusterGroupForm(BootstrapMixin, forms.ModelForm):
slug = SlugField()
class Meta:
model = ClusterGroup
fields = ['name', 'slug']
#
# Clusters
#
class ClusterForm(BootstrapMixin, CustomFieldForm):
class Meta:
model = Cluster
fields = ['name', 'type', 'group']
class ClusterCSVForm(forms.ModelForm):
type = forms.ModelChoiceField(
queryset=ClusterType.objects.all(),
to_field_name='name',
help_text='Name of cluster type',
error_messages={
'invalid_choice': 'Invalid cluster type name.',
}
)
group = forms.ModelChoiceField(
queryset=ClusterGroup.objects.all(),
to_field_name='name',
required=False,
help_text='Name of cluster group',
error_messages={
'invalid_choice': 'Invalid cluster group name.',
}
)
class Meta:
model = Cluster
fields = ['name', 'type', 'group', 'comments']
class ClusterBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(queryset=Cluster.objects.all(), widget=forms.MultipleHiddenInput)
type = forms.ModelChoiceField(queryset=ClusterType.objects.all(), required=False)
group = forms.ModelChoiceField(queryset=ClusterGroup.objects.all(), required=False)
comments = CommentField(widget=SmallTextarea)
class Meta:
nullable_fields = ['group', 'comments']
class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Cluster
q = forms.CharField(required=False, label='Search')
group = FilterChoiceField(
queryset=ClusterGroup.objects.annotate(filter_count=Count('clusters')),
to_field_name='slug',
null_option=(0, 'None'),
required=False,
)
type = FilterChoiceField(
queryset=ClusterType.objects.annotate(filter_count=Count('clusters')),
to_field_name='slug',
required=False,
)
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'),
),
label='Device',
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, *args, **kwargs):
super(ClusterAddDevicesForm, self).__init__(*args, **kwargs)
self.fields['devices'].choices = []
class ClusterRemoveDevicesForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput)
#
# Virtual Machines
#
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', '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 VirtualMachineCSVForm(forms.ModelForm):
status = CSVChoiceField(
choices=STATUS_CHOICES,
required=False,
help_text='Operational status of device'
)
cluster = forms.ModelChoiceField(
queryset=Cluster.objects.all(),
to_field_name='name',
help_text='Name of parent cluster',
error_messages={
'invalid_choice': 'Invalid cluster name.',
}
)
tenant = forms.ModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text='Name of assigned tenant',
error_messages={
'invalid_choice': 'Tenant not found.'
}
)
platform = forms.ModelChoiceField(
queryset=Platform.objects.all(),
required=False,
to_field_name='name',
help_text='Name of assigned platform',
error_messages={
'invalid_choice': 'Invalid platform.',
}
)
class Meta:
model = VirtualMachine
fields = ['name', 'status', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput)
status = forms.ChoiceField(choices=add_blank_choice(STATUS_CHOICES), required=False, initial='')
cluster = forms.ModelChoiceField(queryset=Cluster.objects.all(), required=False)
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
platform = forms.ModelChoiceField(queryset=Platform.objects.all(), required=False)
vcpus = forms.IntegerField(required=False, label='vCPUs')
memory = forms.IntegerField(required=False, label='Memory (MB)')
disk = forms.IntegerField(required=False, label='Disk (GB)')
comments = CommentField(widget=SmallTextarea)
class Meta:
nullable_fields = ['tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
def vm_status_choices():
status_counts = {}
for status in VirtualMachine.objects.values('status').annotate(count=Count('status')).order_by('status'):
status_counts[status['status']] = status['count']
return [(s[0], '{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in STATUS_CHOICES]
class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = VirtualMachine
q = forms.CharField(required=False, label='Search')
cluster_group = FilterChoiceField(
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
null_option=(0, 'None'),
)
cluster_id = FilterChoiceField(
queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
label='Cluster'
)
status = forms.MultipleChoiceField(choices=vm_status_choices, required=False)
#
# VM interfaces
#
class InterfaceForm(BootstrapMixin, forms.ModelForm):
class Meta:
model = Interface
fields = ['virtual_machine', 'name', 'form_factor', 'enabled', 'mac_address', 'mtu', 'description']
widgets = {
'virtual_machine': forms.HiddenInput(),
'form_factor': forms.HiddenInput(),
}
class InterfaceCreateForm(ComponentForm):
name_pattern = ExpandableNameField(label='Name')
form_factor = forms.ChoiceField(choices=VIFACE_FF_CHOICES, initial=IFACE_FF_VIRTUAL, widget=forms.HiddenInput())
enabled = forms.BooleanField(required=False)
mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
mac_address = MACAddressFormField(required=False, label='MAC Address')
description = forms.CharField(max_length=100, required=False)
def __init__(self, *args, **kwargs):
# Set interfaces enabled by default
kwargs['initial'] = kwargs.get('initial', {}).copy()
kwargs['initial'].update({'enabled': True})
super(InterfaceCreateForm, self).__init__(*args, **kwargs)
class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
virtual_machine = forms.ModelChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.HiddenInput)
enabled = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
description = forms.CharField(max_length=100, required=False)
class Meta:
nullable_fields = ['mtu', 'description']
#
# Bulk VirtualMachine component creation
#
class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput)
name_pattern = ExpandableNameField(label='Name')
class VirtualMachineBulkAddInterfaceForm(VirtualMachineBulkAddComponentForm):
form_factor = forms.ChoiceField(choices=VIFACE_FF_CHOICES, initial=IFACE_FF_VIRTUAL, widget=forms.HiddenInput())
enabled = forms.BooleanField(required=False, initial=True)
mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
description = forms.CharField(max_length=100, required=False)