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,
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
):
34 Parent class that holds all vm clean methods
35 and shared form fields.
37 memory
= DataVolumeField(label
=_('Memory'), min_value
=100)
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
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
60 msg
= _("Owner cannot be changed when recovering a failed deployment")
61 self
._errors
["owner"] = self
.error_class([msg
])
63 raise ValidationError(_("Hostname is already in use for this cluster"))
65 except VirtualMachine
.DoesNotExist
:
66 # doesn't exist, no further checks needed
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"])
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
])
87 def clean_security_domain(self
):
88 data
= self
.cleaned_data
['security_domain']
89 security_model
= self
.cleaned_data
['security_model']
92 if data
and security_model
!= 'user':
94 'This field can not be set if Security Mode is not set to User')
95 elif security_model
== 'user':
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')
102 self
._errors
['security_domain'] = self
.error_class([msg
])
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
])
113 class NewVirtualMachineForm(VirtualMachineForm
):
115 Virtual Machine Creation form
117 pvm_exclude_fields
= ('disk_type','nic_type', 'boot_order', 'serial_console',
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'),
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
):
144 super(NewVirtualMachineForm
, self
).__init
__(initial
, *args
, **kwargs
)
148 if 'cluster' in initial
and initial
['cluster']:
150 cluster
= Cluster
.objects
.get(pk
=initial
['cluster'])
151 except Cluster
.DoesNotExist
:
152 # defer to clean function to return errors
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)
167 defaults
= cluster_default_info(cluster
, hv
)
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'],
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
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']
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']:
205 self
.owner
= ClusterUser
.objects
.get(pk
=initial
['owner']).cast()
206 except ClusterUser
.DoesNotExist
:
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()
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
233 q
= self
.owner
.get_objects_any_perms(Cluster
, ['admin','create_vm'])
235 q
= user
.get_objects_any_perms(Cluster
, ['admin','create_vm'])
236 self
.fields
['cluster'].queryset
= q
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
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':
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"]
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']
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.
298 # From this point, database stuff is alright.
302 if isinstance(owner
, (Organization
,)):
303 grantee
= owner
.group
306 data
['grantee'] = grantee
308 # superusers bypass all permission and quota checks
309 if not self
.user
.is_superuser
and owner
:
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")
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")
329 start
= data
['start']
330 quota
= cluster
.get_quota(owner
)
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']):
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']):
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
])
353 self
._errors
["owner"] = self
.error_class([msg
])
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
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':
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"]
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']
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')
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
])
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
])
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.
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
433 if owner
is not None:
434 start
= data
['start']
435 quota
= cluster
.get_quota(owner
)
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']):
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']):
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
):
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
])
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
)
483 except AttributeError:
484 self
.owner
= vm
.owner
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
496 for field
in self
.required
:
497 self
.fields
[field
].required
= True
498 except AttributeError:
501 # Need to set initial values from vm.info as these are not saved
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
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']
516 if self
.hvparam_fields
:
517 for field
in self
.hvparam_fields
:
518 self
.fields
[field
].initial
= hvparam
.get(field
)
519 except AttributeError:
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
])
546 data
['start'] = 'reboot' in self
.data
or self
.vm
.is_running
547 check_quota_modify(self
)
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',
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',
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',
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,
614 kvm_flag
= forms
.ChoiceField(label
='KVM Flag', required
=False,
616 mem_path
= forms
.CharField(label
='Mem Path', required
=False)
617 migration_downtime
= forms
.IntegerField(label
='Migration Downtime',
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,
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',
630 class Meta(ModifyVirtualMachineForm
.Meta
):
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
):
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
)
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,
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
):
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"))
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'))