123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- from __future__ import unicode_literals
- from mptt.forms import TreeNodeChoiceField
- from django import forms
- from django.core.exceptions import ValidationError
- from django.db.models import Count
- from dcim.constants import IFACE_FF_VIRTUAL
- from dcim.formfields import MACAddressFormField
- from dcim.models import Device, DeviceRole, 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
- VIFACE_FF_CHOICES = (
- (IFACE_FF_VIRTUAL, 'Virtual'),
- )
- #
- # 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):
- comments = CommentField(widget=SmallTextarea)
- class Meta:
- model = Cluster
- fields = ['name', 'type', 'group', 'site', 'comments']
- 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.',
- }
- )
- site = forms.ModelChoiceField(
- queryset=Site.objects.all(),
- to_field_name='name',
- required=False,
- help_text='Name of assigned site',
- error_messages={
- 'invalid_choice': 'Invalid site name.',
- }
- )
- class Meta:
- model = Cluster
- fields = ['name', 'type', 'group', 'site', '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)
- site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False)
- comments = CommentField(widget=SmallTextarea)
- class Meta:
- nullable_fields = ['group', 'site', 'comments']
- class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
- model = Cluster
- q = forms.CharField(required=False, label='Search')
- type = FilterChoiceField(
- queryset=ClusterType.objects.annotate(filter_count=Count('clusters')),
- to_field_name='slug',
- required=False,
- )
- group = FilterChoiceField(
- queryset=ClusterGroup.objects.annotate(filter_count=Count('clusters')),
- to_field_name='slug',
- null_option=(0, 'None'),
- required=False,
- )
- site = FilterChoiceField(
- queryset=Site.objects.annotate(filter_count=Count('clusters')),
- to_field_name='slug',
- null_option=(0, 'None'),
- 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, 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 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', '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 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.',
- }
- )
- role = forms.ModelChoiceField(
- queryset=DeviceRole.objects.filter(vm_role=True),
- required=False,
- to_field_name='name',
- help_text='Name of functional role',
- error_messages={
- 'invalid_choice': 'Invalid role 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', 'role', '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)
- role = forms.ModelChoiceField(queryset=DeviceRole.objects.filter(vm_role=True), 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 = ['role', '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'
- )
- role = FilterChoiceField(
- queryset=DeviceRole.objects.filter(vm_role=True).annotate(filter_count=Count('virtual_machines')),
- to_field_name='slug',
- null_option=(0, 'None')
- )
- status = forms.MultipleChoiceField(choices=vm_status_choices, required=False)
- tenant = FilterChoiceField(
- queryset=Tenant.objects.annotate(filter_count=Count('virtual_machines')),
- to_field_name='slug',
- null_option=(0, 'None')
- )
- platform = FilterChoiceField(
- queryset=Platform.objects.annotate(filter_count=Count('virtual_machines')),
- to_field_name='slug',
- null_option=(0, 'None')
- )
- #
- # 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)
|