Merge branch 'develop' into feature/app_rename
[ganeti_webmgr.git] / ganeti_web / forms / virtual_machine.py
bloba3028e922051ec9d3592238ef5809df26d48d7fe
1 # Copyright (C) 2010 Oregon State University et al.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
16 # USA.
17 import copy
19 from django import forms
20 from django.forms import ValidationError
21 from django.utils import simplejson
23 from ganeti_web import constants
24 from ganeti_web.fields import DataVolumeField
25 from ganeti_web.models import (Cluster, ClusterUser, Organization,
26 VirtualMachineTemplate, VirtualMachine)
27 from ganeti_web.utilities import cluster_default_info, cluster_os_list, contains
28 from django.utils.translation import ugettext_lazy as _
32 class VirtualMachineForm(forms.ModelForm):
33 """
34 Parent class that holds all vm clean methods
35 and shared form fields.
36 """
37 memory = DataVolumeField(label=_('Memory'), min_value=100)
39 class Meta:
40 model = VirtualMachineTemplate
42 def clean_hostname(self):
43 data = self.cleaned_data
44 hostname = data.get('hostname')
45 cluster = data.get('cluster')
46 if hostname and cluster:
47 # Verify that this hostname is not in use for this cluster. It can
48 # only be reused when recovering a VM that failed to deploy.
50 # Recoveries are only allowed when the user is the owner of the VM
51 try:
52 vm = VirtualMachine.objects.get(cluster=cluster, hostname=hostname)
54 # detect vm that failed to deploy
55 if not vm.pending_delete and vm.template is not None:
56 current_owner = vm.owner.cast()
57 if current_owner == self.owner:
58 data['vm_recovery'] = vm
59 else:
60 msg = _("Owner cannot be changed when recovering a failed deployment")
61 self._errors["owner"] = self.error_class([msg])
62 else:
63 raise ValidationError(_("Hostname is already in use for this cluster"))
65 except VirtualMachine.DoesNotExist:
66 # doesn't exist, no further checks needed
67 pass
69 return hostname
71 def clean_vcpus(self):
72 vcpus = self.cleaned_data.get("vcpus")
74 if vcpus is None or vcpus < 1:
75 self._errors["vcpus"] = self.error_class(
76 ["At least one CPU must be present"])
77 else:
78 return vcpus
80 def clean_initrd_path(self):
81 data = self.cleaned_data['initrd_path']
82 if data and not data.startswith('/') and data != 'no_initrd_path':
83 msg = u"%s." % _('This field must start with a "/"')
84 self._errors['initrd_path'] = self.error_class([msg])
85 return data
87 def clean_security_domain(self):
88 data = self.cleaned_data['security_domain']
89 security_model = self.cleaned_data['security_model']
90 msg = None
92 if data and security_model != 'user':
93 msg = u'%s.' % _(
94 'This field can not be set if Security Mode is not set to User')
95 elif security_model == 'user':
96 if not data:
97 msg = u'%s.' % _('This field is required')
98 elif not data[0].isalpha():
99 msg = u'%s.' % _('This field must being with an alpha character')
101 if msg:
102 self._errors['security_domain'] = self.error_class([msg])
103 return data
105 def clean_vnc_x509_path(self):
106 data = self.cleaned_data['vnc_x509_path']
107 if data and not data.startswith('/'):
108 msg = u'%s,' % _('This field must start with a "/"')
109 self._errors['vnc_x509_path'] = self.error_class([msg])
110 return data
113 class NewVirtualMachineForm(VirtualMachineForm):
115 Virtual Machine Creation form
117 pvm_exclude_fields = ('disk_type','nic_type', 'boot_order', 'serial_console',
118 'cdrom_image_path')
120 empty_field = constants.EMPTY_CHOICE_FIELD
121 templates = constants.HV_DISK_TEMPLATES
122 nicmodes = constants.HV_NIC_MODES
124 owner = forms.ModelChoiceField(queryset=ClusterUser.objects.all(), label=_('Owner'))
125 cluster = forms.ModelChoiceField(queryset=Cluster.objects.none(), label=_('Cluster'))
126 hypervisor = forms.ChoiceField(required=False, choices=[empty_field])
127 hostname = forms.CharField(label=_('Instance Name'), max_length=255)
128 pnode = forms.ChoiceField(label=_('Primary Node'), choices=[empty_field])
129 snode = forms.ChoiceField(label=_('Secondary Node'), choices=[empty_field])
130 os = forms.ChoiceField(label=_('Operating System'), choices=[empty_field])
131 disk_template = forms.ChoiceField(label=_('Disk Template'),
132 choices=templates)
133 disk_size = DataVolumeField(label=_('Disk Size'), min_value=100)
134 disk_type = forms.ChoiceField(label=_('Disk Type'), choices=[empty_field])
135 nic_mode = forms.ChoiceField(label=_('NIC Mode'), choices=nicmodes)
136 nic_type = forms.ChoiceField(label=_('NIC Type'), choices=[empty_field])
137 boot_order = forms.ChoiceField(label=_('Boot Device'), choices=[empty_field])
139 class Meta(VirtualMachineForm.Meta):
140 exclude = ('template_name')
142 def __init__(self, user, initial=None, *args, **kwargs):
143 self.user = user
144 super(NewVirtualMachineForm, self).__init__(initial, *args, **kwargs)
146 cluster = None
147 if initial:
148 if 'cluster' in initial and initial['cluster']:
149 try:
150 cluster = Cluster.objects.get(pk=initial['cluster'])
151 except Cluster.DoesNotExist:
152 # defer to clean function to return errors
153 pass
154 if cluster is not None:
155 # set choices based on selected cluster if given
156 oslist = cluster_os_list(cluster)
157 nodelist = [str(h) for h in cluster.nodes.values_list('hostname', flat=True)]
158 nodes = zip(nodelist, nodelist)
159 nodes.insert(0, self.empty_field)
160 oslist.insert(0, self.empty_field)
161 self.fields['pnode'].choices = nodes
162 self.fields['snode'].choices = nodes
163 self.fields['os'].choices = oslist
165 hv = initial.get('hypervisor', None)
166 if hv is not None:
167 defaults = cluster_default_info(cluster, hv)
168 else:
169 defaults = cluster_default_info(cluster)
170 hv = defaults['hypervisor']
171 if defaults['iallocator'] != '' :
172 self.fields['iallocator'].initial = True
173 self.fields['iallocator_hostname'] = forms.CharField(
174 initial=defaults['iallocator'],
175 required=False,
176 widget = forms.HiddenInput())
177 self.fields['vcpus'].initial = defaults['vcpus']
178 self.fields['memory'].initial = defaults['memory']
179 self.fields['nic_link'].initial = defaults['nic_link']
180 self.fields['hypervisor'].choices = defaults['hypervisors']
181 self.fields['hypervisor'].initial = hv
183 if hv == 'kvm':
184 self.fields['serial_console'].initial = defaults['serial_console']
186 # Set field choices and hypervisor
187 if hv == 'kvm' or hv == 'xen-pvm':
188 self.fields['root_path'].initial = defaults['root_path']
189 self.fields['kernel_path'].initial = defaults['kernel_path']
190 if hv == 'kvm' or hv == 'xen-hvm':
191 self.fields['nic_type'].choices = defaults['nic_types']
192 self.fields['disk_type'].choices = defaults['disk_types']
193 self.fields['boot_order'].choices = defaults['boot_devices']
195 self.fields['nic_type'].initial = defaults['nic_type']
196 self.fields['disk_type'].initial = defaults['disk_type']
197 self.fields['boot_order'].initial = defaults['boot_order']
198 if hv == 'xen-pvm':
199 for field in self.pvm_exclude_fields:
200 del self.fields[field]
202 # set cluster choices based on the given owner
203 if initial and 'owner' in initial and initial['owner']:
204 try:
205 self.owner = ClusterUser.objects.get(pk=initial['owner']).cast()
206 except ClusterUser.DoesNotExist:
207 self.owner = None
208 else:
209 self.owner = None
211 # Set up owner and cluster choices.
212 if user.is_superuser:
213 # Superusers may do whatever they like.
214 self.fields['owner'].queryset = ClusterUser.objects.all()
215 self.fields['cluster'].queryset = Cluster.objects.all()
216 else:
217 # Fill out owner choices. Remember, the list of owners is a list
218 # of tuple(ClusterUser.id, label). If you put ids from other
219 # Models into this, no magical correction will be applied and you
220 # will assign permissions to the wrong owner; see #2007.
221 owners = [(u'', u'---------')]
222 for group in user.groups.all():
223 owners.append((group.organization.id, group.name))
224 if user.has_any_perms(Cluster, ['admin','create_vm'], False):
225 profile = user.get_profile()
226 owners.append((profile.id, profile.name))
227 self.fields['owner'].choices = owners
229 # Set cluster choices. If an owner has been selected then filter
230 # by the owner. Otherwise show everything the user has access to
231 # through themselves or any groups they are a member of
232 if self.owner:
233 q = self.owner.get_objects_any_perms(Cluster, ['admin','create_vm'])
234 else:
235 q = user.get_objects_any_perms(Cluster, ['admin','create_vm'])
236 self.fields['cluster'].queryset = q
238 def clean(self):
239 data = self.cleaned_data
241 # First things first. Let's do any error-checking and validation which
242 # requires combinations of data but doesn't require hitting the DB.
244 # Check that, if we are on any disk template but diskless, our
245 # disk_size is set and greater than zero.
246 if data.get("disk_template") != "diskless":
247 if not data.get("disk_size", 0):
248 self._errors["disk_size"] = self.error_class(
249 [u"Disk size must be set and greater than zero"])
251 pnode = data.get("pnode", '')
252 snode = data.get("snode", '')
253 iallocator = data.get('iallocator', False)
254 iallocator_hostname = data.get('iallocator_hostname', '')
255 disk_template = data.get("disk_template")
257 # Need to have pnode != snode
258 if disk_template == "drbd" and not iallocator:
259 if pnode == snode and (pnode != '' or snode != ''):
260 # We know these are not in self._errors now
261 msg = u"%s." % _("Primary and Secondary Nodes must not match")
262 self._errors["pnode"] = self.error_class([msg])
264 # These fields are no longer valid. Remove them from the
265 # cleaned data.
266 del data["pnode"]
267 del data["snode"]
268 else:
269 if "snode" in self._errors:
270 del self._errors["snode"]
272 # If boot_order = CD-ROM make sure imagepath is set as well.
273 boot_order = data.get('boot_order', '')
274 image_path = data.get('cdrom_image_path', '')
275 if boot_order == 'cdrom':
276 if image_path == '':
277 msg = u"%s." % _("Image path required if boot device is CD-ROM")
278 self._errors["cdrom_image_path"] = self.error_class([msg])
279 del data["cdrom_image_path"]
281 if iallocator:
282 # If iallocator is checked,
283 # don't display error messages for nodes
284 if iallocator_hostname != '':
285 if 'pnode' in self._errors:
286 del self._errors['pnode']
287 if 'snode' in self._errors:
288 del self._errors['snode']
289 else:
290 msg = u"%s." % _(
291 "Automatic Allocation was selected, but there is no IAllocator available.")
292 self._errors['iallocator'] = self.error_class([msg])
294 # If there are any errors, exit early.
295 if self._errors:
296 return data
298 # From this point, database stuff is alright.
300 owner = self.owner
301 if owner:
302 if isinstance(owner, (Organization,)):
303 grantee = owner.group
304 else:
305 grantee = owner.user
306 data['grantee'] = grantee
308 # superusers bypass all permission and quota checks
309 if not self.user.is_superuser and owner:
310 msg = None
312 if isinstance(owner, (Organization,)):
313 # check user membership in group if group
314 if not grantee.user_set.filter(id=self.user.id).exists():
315 msg = u"%s." % _("User is not a member of the specified group")
317 else:
318 if not owner.user_id == self.user.id:
319 msg = u"%s." % _("You are not allowed to act on behalf of this user")
321 # check permissions on cluster
322 if 'cluster' in data:
323 cluster = data['cluster']
324 if not (owner.has_perm('create_vm', cluster)
325 or owner.has_perm('admin', cluster)):
326 msg = u"%s." % _("Owner does not have permissions for this cluster")
328 # check quota
329 start = data['start']
330 quota = cluster.get_quota(owner)
331 if quota.values():
332 used = owner.used_resources(cluster, only_running=True)
334 if (start and quota['ram'] is not None and
335 (used['ram'] + data['memory']) > quota['ram']):
336 del data['memory']
337 q_msg = u"%s" % _("Owner does not have enough ram remaining on this cluster. You may choose to not automatically start the instance or reduce the amount of ram.")
338 self._errors["ram"] = self.error_class([q_msg])
340 if quota['disk'] and used['disk'] + data['disk_size'] > quota['disk']:
341 del data['disk_size']
342 q_msg = u"%s" % _("Owner does not have enough diskspace remaining on this cluster.")
343 self._errors["disk_size"] = self.error_class([q_msg])
345 if (start and quota['virtual_cpus'] is not None and
346 (used['virtual_cpus'] + data['vcpus']) >
347 quota['virtual_cpus']):
348 del data['vcpus']
349 q_msg = u"%s" % _("Owner does not have enough virtual cpus remaining on this cluster. You may choose to not automatically start the instance or reduce the amount of virtual cpus.")
350 self._errors["vcpus"] = self.error_class([q_msg])
352 if msg:
353 self._errors["owner"] = self.error_class([msg])
354 del data['owner']
356 pnode = data.get("pnode", '')
357 snode = data.get("snode", '')
358 iallocator = data.get('iallocator', False)
359 iallocator_hostname = data.get('iallocator_hostname', '')
360 disk_template = data.get("disk_template")
362 # Need to have pnode != snode
363 if disk_template == "drbd" and not iallocator:
364 if pnode == snode and (pnode != '' or snode != ''):
365 # We know these are not in self._errors now
366 msg = u"%s." % _("Primary and Secondary Nodes must not match")
367 self._errors["pnode"] = self.error_class([msg])
369 # These fields are no longer valid. Remove them from the
370 # cleaned data.
371 del data["pnode"]
372 del data["snode"]
373 else:
374 if "snode" in self._errors:
375 del self._errors["snode"]
377 # If boot_order = CD-ROM make sure imagepath is set as well.
378 boot_order = data.get('boot_order', '')
379 image_path = data.get('cdrom_image_path', '')
380 if boot_order == 'cdrom':
381 if image_path == '':
382 msg = u"%s." % _("Image path required if boot device is CD-ROM")
383 self._errors["cdrom_image_path"] = self.error_class([msg])
384 del data["cdrom_image_path"]
386 if iallocator:
387 # If iallocator is checked,
388 # don't display error messages for nodes
389 if iallocator_hostname != '':
390 if 'pnode' in self._errors:
391 del self._errors['pnode']
392 if 'snode' in self._errors:
393 del self._errors['snode']
394 else:
395 msg = u"%s." % _("Automatic Allocation was selected, but there is no \
396 IAllocator available.")
397 self._errors['iallocator'] = self.error_class([msg])
399 # Check options which depend on the the hypervisor type
400 hv = data.get('hypervisor')
401 disk_type = data.get('disk_type')
402 nic_type = data.get('nic_type')
404 # Check disk_type
405 if (hv == 'kvm' and not (contains(disk_type, constants.KVM_DISK_TYPES) or contains(disk_type, constants.HV_DISK_TYPES))) or \
406 (hv == 'xen-hvm' and not (contains(disk_type, constants.HVM_DISK_TYPES) or contains(disk_type, constants.HV_DISK_TYPES))):
407 msg = '%s is not a valid option for Disk Template on this cluster.' % disk_type
408 self._errors['disk_type'] = self.error_class([msg])
409 # Check nic_type
410 if (hv == 'kvm' and not (contains(nic_type, constants.KVM_NIC_TYPES) or \
411 contains(nic_type, constants.HV_NIC_TYPES))) or \
412 (hv == 'xen-hvm' and not contains(nic_type, constants.HV_NIC_TYPES)):
413 msg = '%s is not a valid option for Nic Type on this cluster.' % nic_type
414 self._errors['nic_type'] = self.error_class([msg])
415 # Check boot_order
416 if (hv == 'kvm' and not contains(boot_order, constants.KVM_BOOT_ORDER)) or \
417 (hv == 'xen-hvm' and not contains(boot_order, constants.HVM_BOOT_ORDER)):
418 msg = '%s is not a valid option for Boot Device on this cluster.' % boot_order
419 self._errors['boot_order'] = self.error_class([msg])
421 # Always return the full collection of cleaned data.
422 return data
425 def check_quota_modify(form):
426 """ method for validating user is within their quota when modifying """
427 data = form.cleaned_data
428 cluster = form.cluster
429 owner = form.owner
430 vm = form.vm
432 # check quota
433 if owner is not None:
434 start = data['start']
435 quota = cluster.get_quota(owner)
436 if quota.values():
437 used = owner.used_resources(cluster, only_running=True)
439 if (start and quota['ram'] is not None and
440 (used['ram'] + data['memory']-vm.ram) > quota['ram']):
441 del data['memory']
442 q_msg = u"%s" % _("Owner does not have enough ram remaining on this cluster. You must reduce the amount of ram.")
443 form._errors["ram"] = form.error_class([q_msg])
445 if 'disk_size' in data and data['disk_size']:
446 if quota['disk'] and used['disk'] + data['disk_size'] > quota['disk']:
447 del data['disk_size']
448 q_msg = u"%s" % _("Owner does not have enough diskspace remaining on this cluster.")
449 form._errors["disk_size"] = form.error_class([q_msg])
451 if (start and quota['virtual_cpus'] is not None and
452 (used['virtual_cpus'] + data['vcpus'] - vm.virtual_cpus) >
453 quota['virtual_cpus']):
454 del data['vcpus']
455 q_msg = u"%s" % _("Owner does not have enough virtual cpus remaining on this cluster. You must reduce the amount of virtual cpus.")
456 form._errors["vcpus"] = form.error_class([q_msg])
459 class ModifyVirtualMachineForm(VirtualMachineForm):
461 Base modify class.
462 If hvparam_fields (itirable) set on child, then
463 each field on the form will be initialized to the
464 value in vm.info.hvparams
466 always_required = ('vcpus', 'memory')
467 empty_field = constants.EMPTY_CHOICE_FIELD
469 nic_mac = forms.CharField(label=_('NIC Mac'), required=False)
470 os = forms.ChoiceField(label=_('Operating System'), choices=[empty_field])
472 class Meta:
473 model = VirtualMachineTemplate
474 exclude = ('start', 'owner', 'cluster', 'hostname', 'name_check',
475 'iallocator', 'iallocator_hostname', 'disk_template', 'pnode',
476 'snode','disk_size', 'nic_mode', 'template_name', 'hypervisor')
478 def __init__(self, vm, *args, **kwargs):
479 super(VirtualMachineForm, self).__init__(*args, **kwargs)
480 # Set owner on form
481 try:
482 self.owner
483 except AttributeError:
484 self.owner = vm.owner
486 # Setup os choices
487 os_list = cluster_os_list(vm.cluster)
488 self.fields['os'].choices = os_list
490 for field in self.always_required:
491 self.fields[field].required = True
492 # If the required property is set on a child class,
493 # require those form fields
494 try:
495 if self.required:
496 for field in self.required:
497 self.fields[field].required = True
498 except AttributeError:
499 pass
501 # Need to set initial values from vm.info as these are not saved
502 # per the vm model.
503 if vm.info:
504 info = vm.info
505 hvparam = info['hvparams']
506 # XXX Convert ram string since it comes out
507 # from ganeti as an int and the DataVolumeField does not like
508 # ints.
509 self.fields['vcpus'].initial = info['beparams']['vcpus']
510 self.fields['memory'].initial = str(info['beparams']['memory'])
511 self.fields['nic_link'].initial = info['nic.links'][0]
512 self.fields['nic_mac'].initial = info['nic.macs'][0]
513 self.fields['os'].initial = info['os']
515 try:
516 if self.hvparam_fields:
517 for field in self.hvparam_fields:
518 self.fields[field].initial = hvparam.get(field)
519 except AttributeError:
520 pass
522 def clean(self):
523 data = self.cleaned_data
524 kernel_path = data.get('kernel_path')
525 initrd_path = data.get('initrd_path')
527 # Makesure if initrd_path is set, kernel_path is aswell
528 if initrd_path and not kernel_path:
529 msg = u"%s." % _("Kernel Path must be specified along with Initrd Path")
530 self._errors['kernel_path'] = self.error_class([msg])
531 self._errors['initrd_path'] = self.error_class([msg])
532 del data['initrd_path']
534 vnc_tls = data.get('vnc_tls')
535 vnc_x509_path = data.get('vnc_x509_path')
536 vnc_x509_verify = data.get('vnc_x509_verify')
538 if not vnc_tls and vnc_x509_path:
539 msg = u'%s.' % _('This field can not be set without VNC TLS enabled')
540 self._errors['vnc_x509_path'] = self.error_class([msg])
541 if vnc_x509_verify and not vnc_x509_path:
542 msg = u'%s.' % _('This field is required')
543 self._errors['vnc_x509_path'] = self.error_class([msg])
545 if self.owner:
546 data['start'] = 'reboot' in self.data or self.vm.is_running
547 check_quota_modify(self)
548 del data['start']
550 return data
553 class HvmModifyVirtualMachineForm(ModifyVirtualMachineForm):
554 hvparam_fields = ('boot_order', 'cdrom_image_path', 'nic_type',
555 'disk_type', 'vnc_bind_address', 'acpi', 'use_localtime')
556 required = ('disk_type', 'boot_order', 'nic_type')
557 empty_field = constants.EMPTY_CHOICE_FIELD
558 disk_types = constants.HVM_CHOICES['disk_type']
559 nic_types = constants.HVM_CHOICES['nic_type']
560 boot_devices = constants.HVM_CHOICES['boot_order']
562 acpi = forms.BooleanField(label='ACPI', required=False)
563 use_localtime = forms.BooleanField(label='Use Localtime', required=False)
564 vnc_bind_address = forms.IPAddressField(label='VNC Bind Address',
565 required=False)
566 disk_type = forms.ChoiceField(label=_('Disk Type'), choices=disk_types)
567 nic_type = forms.ChoiceField(label=_('NIC Type'), choices=nic_types)
568 boot_order = forms.ChoiceField(label=_('Boot Device'), choices=boot_devices)
570 class Meta(ModifyVirtualMachineForm.Meta):
571 exclude = ModifyVirtualMachineForm.Meta.exclude + ('kernel_path',
572 'root_path', 'kernel_args', 'serial_console')
574 def __init__(self, vm, *args, **kwargs):
575 super(HvmModifyVirtualMachineForm, self).__init__(vm, *args, **kwargs)
577 class PvmModifyVirtualMachineForm(ModifyVirtualMachineForm):
578 hvparam_fields = ('root_path', 'kernel_path', 'kernel_args',
579 'initrd_path')
581 initrd_path = forms.CharField(label='initrd Path', required=False)
582 kernel_args = forms.CharField(label='Kernel Args', required=False)
584 class Meta(ModifyVirtualMachineForm.Meta):
585 exclude = ModifyVirtualMachineForm.Meta.exclude + ('disk_type',
586 'nic_type', 'boot_order', 'cdrom_image_path', 'serial_console')
588 def __init__(self, vm, *args, **kwargs):
589 super(PvmModifyVirtualMachineForm, self).__init__(vm, *args, **kwargs)
592 class KvmModifyVirtualMachineForm(PvmModifyVirtualMachineForm,
593 HvmModifyVirtualMachineForm):
594 hvparam_fields = ('acpi', 'disk_cache', 'initrd_path',
595 'kernel_args', 'kvm_flag', 'mem_path',
596 'migration_downtime', 'security_domain',
597 'security_model', 'usb_mouse', 'use_chroot',
598 'use_localtime', 'vnc_bind_address', 'vnc_tls',
599 'vnc_x509_path', 'vnc_x509_verify', 'disk_type',
600 'boot_order', 'nic_type', 'root_path',
601 'kernel_path', 'serial_console',
602 'cdrom_image_path',
604 disk_caches = constants.HV_DISK_CACHES
605 kvm_flags = constants.KVM_FLAGS
606 security_models = constants.HV_SECURITY_MODELS
607 usb_mice = constants.HV_USB_MICE
608 disk_types = constants.KVM_CHOICES['disk_type']
609 nic_types = constants.KVM_CHOICES['nic_type']
610 boot_devices = constants.KVM_CHOICES['boot_order']
612 disk_cache = forms.ChoiceField(label='Disk Cache', required=False,
613 choices=disk_caches)
614 kvm_flag = forms.ChoiceField(label='KVM Flag', required=False,
615 choices=kvm_flags)
616 mem_path = forms.CharField(label='Mem Path', required=False)
617 migration_downtime = forms.IntegerField(label='Migration Downtime',
618 required=False)
619 security_model = forms.ChoiceField(label='Security Model',
620 required=False, choices=security_models)
621 security_domain = forms.CharField(label='Security Domain', required=False)
622 usb_mouse = forms.ChoiceField(label='USB Mouse', required=False,
623 choices=usb_mice)
624 use_chroot = forms.BooleanField(label='Use Chroot', required=False)
625 vnc_tls = forms.BooleanField(label='VNC TLS', required=False)
626 vnc_x509_path = forms.CharField(label='VNC x509 Path', required=False)
627 vnc_x509_verify = forms.BooleanField(label='VNC x509 Verify',
628 required=False)
630 class Meta(ModifyVirtualMachineForm.Meta):
631 pass
633 def __init__(self, vm, *args, **kwargs):
634 super(KvmModifyVirtualMachineForm, self).__init__(vm, *args, **kwargs)
635 self.fields['disk_type'].choices = self.disk_types
636 self.fields['nic_type'].choices = self.nic_types
637 self.fields['boot_order'].choices = self.boot_devices
640 class ModifyConfirmForm(forms.Form):
642 def clean(self):
643 raw = self.data['rapi_dict']
644 data = simplejson.loads(raw)
646 cleaned = self.cleaned_data
647 cleaned['rapi_dict'] = data
648 cleaned['memory'] = data['memory']
649 cleaned['vcpus'] = data['vcpus']
650 cleaned['start'] = 'reboot' in data or self.vm.is_running
651 check_quota_modify(self)
653 return cleaned
656 class MigrateForm(forms.Form):
657 """ Form used for migrating a Virtual Machine """
658 mode = forms.ChoiceField(choices=constants.MODE_CHOICES)
659 cleanup = forms.BooleanField(initial=False, required=False,
660 label=_("Attempt recovery from failed migration"))
663 class RenameForm(forms.Form):
664 """ form used for renaming a Virtual Machine """
665 hostname = forms.CharField(label=_('Instance Name'), max_length=255,
666 required=True)
667 ip_check = forms.BooleanField(initial=True, required=False, label=_('IP Check'))
668 name_check = forms.BooleanField(initial=True, required=False, label=_('DNS Name Check'))
670 def __init__(self, vm, *args, **kwargs):
671 self.vm = vm
672 super(RenameForm, self).__init__(*args, **kwargs)
674 def clean_hostname(self):
675 data = self.cleaned_data
676 hostname = data.get('hostname', None)
677 if hostname and hostname == self.vm.hostname:
678 raise ValidationError(_("The new hostname must be different than the current hostname"))
679 return hostname
682 class ChangeOwnerForm(forms.Form):
683 """ Form used when modifying the owner of a virtual machine """
684 owner = forms.ModelChoiceField(queryset=ClusterUser.objects.all(), label=_('Owner'))