22 from cm.settings
import VNC_PORTS, NOVNC_PORTS
24 from cm.models.node
import Node
25 from cm.models.public_ip
import PublicIP
26 from cm.models.template
import Template
27 from cm.models.user
import User
28 from cm.models.farm
import Farm
29 from cm.models.system_image
import SystemImage
30 from cm.models.iso_image
import IsoImage
31 from cm.models.lease
import Lease
32 from cm.models.available_network
import AvailableNetwork
33 from cm.utils.exception
import CMException
34 from cm.utils
import log
35 from cm.utils.monia
import RrdHandler
37 from django.db
import models, transaction
38 from django.conf
import settings
39 from django.template
import loader, Context
40 from django.conf
import settings
as django_settings
42 from common.states
import vm_states, image_states, farm_states, lease_states, vnc_states
43 from common.utils
import password_gen
45 from datetime
import datetime
51 from netaddr
import IPNetwork
52 from cm.utils
import message
69 class VM(models.Model):
70 name = models.CharField(max_length=128)
71 node = models.ForeignKey(Node)
72 user = models.ForeignKey(User)
73 template = models.ForeignKey(Template)
74 system_image = models.ForeignKey(SystemImage)
75 iso_image = models.ForeignKey(IsoImage, null=
True, blank=
True)
76 libvirt_id = models.IntegerField()
77 state = models.IntegerField()
78 start_time = models.DateTimeField()
79 stop_time = models.DateTimeField(null=
True, blank=
True)
80 ctx_key = models.CharField(max_length=128, null=
True, blank=
True)
81 ctx_api_version = models.CharField(max_length=10, null=
True, blank=
True)
82 vnc_passwd = models.CharField(max_length=45)
84 ssh_key = models.TextField(null=
True, blank=
True)
85 ssh_username = models.CharField(max_length=45, null=
True, blank=
True)
86 description = models.TextField(null=
True, blank=
True)
87 save_vm = models.IntegerField()
88 farm = models.ForeignKey(Farm, related_name=
'vms', null=
True)
89 hostname = models.CharField(max_length=256, null=
True, blank=
True)
90 vnc_port = models.IntegerField()
91 novnc_port = models.IntegerField(default=0)
92 vnc_enabled = models.IntegerField(default=0)
93 reservation_id = models.IntegerField(default=0)
94 user_data = models.CharField(max_length=32768, null=
True, blank=
True)
127 d[
'user_id'] = self.user.id
128 d[
'name'] = self.
name
129 d[
'state'] = self.
state
130 d[
'leases'] = [l.dict
for l
in self.lease_set.all()]
131 d[
'image_name'] = self.system_image.name
132 d[
'image_id'] = self.system_image.id
133 d[
'template_name'] = self.template.name
136 d[
'vnc_endpoint'] =
'%s:%d' % (settings.VNC_ADDRESS, self.
vnc_port)
137 d[
'novnc_endpoint'] =
'%s:%d' % (settings.VNC_ADDRESS, self.
novnc_port)
142 d[
'iso_images'] = [{
'id': self.iso_image.id,
'name': self.iso_image.name}]
145 d[
'storage_images'] = [{
'storage_image_id': img.id,
'name': img.name}
for img
in self.
storage_images]
181 d[
'user_id'] = self.user.id
182 d[
'name'] = self.
name
183 d[
'state'] = self.
state
184 d[
'leases'] = [l.dict
for l
in self.lease_set.all()]
185 d[
'image_name'] = self.system_image.name
186 d[
'image_id'] = self.system_image.id
187 d[
'template_name'] = self.template.name
190 d[
'vnc_endpoint'] =
'%s:%d' % (settings.VNC_ADDRESS, self.
vnc_port)
191 d[
'novnc_endpoint'] =
'%s:%d' % (settings.VNC_ADDRESS, self.
novnc_port)
196 d[
'uptime'] = delta.seconds + 24 * 3600 * delta.days
197 d[
'node'] = self.node.address
200 d[
'ssh_key'] = self.
ssh_key or ''
205 d[
'iso_images'] = [{
'id': self.iso_image.id,
'name': self.iso_image.name}]
208 d[
'storage_images'] = [{
'storage_image_id': img.id,
'name': img.name,
'disk_controller': img.disk_controller}
214 def create(user, name, description, image_id, template_id, public_ip_id, iso_list, disk_list, vnc, groups,
216 ssh_username=
None, count=1, farm=
None, head_template_id=
None, node_id=
False, lease_id=
None,
218 from cm.models.storage_image
import StorageImage
220 template = Template.get(template_id)
221 image = SystemImage.get(user.id, image_id, groups)
223 if image.state != image_states[
'ok']:
224 raise CMException(
'image_unavailable')
227 head_template = Template.get(head_template_id)
228 wn_template = template
229 user.check_quota([(head_template, 1), (wn_template, count)])
232 user.check_quota([(template, count)])
236 reservation_id =
None
238 for i
in range(count):
239 log.debug(user.id,
"Looking for node")
240 node = Node.get_free_node(template, image, node_id)
241 log.info(user.id,
'Selected node: %d' % node.id)
247 vm.name =
'%s-head' % name
248 vm.description =
'Farm head'
249 vm.template = head_template
251 vm.name =
'%s-wn%d' % (name, i)
252 vm.description =
'Worker Node'
253 vm.template = wn_template
255 vm.template = template
256 vm.description = description
258 vm.name =
'%s_%d' % (name, i + 1)
262 vm.state = vm_states[
'init']
263 vm.start_time = datetime.now()
264 vm.system_image = image
271 used_ports = VM.objects.exclude(state__in=[vm_states[
'closed'], vm_states[
'erased']]).values_list(
'vnc_port', flat=
True)
273 for new_vnc_port
in xrange(VNC_PORTS[
'START'], VNC_PORTS[
'END'] + 1):
274 if new_vnc_port
not in used_ports
and new_vnc_port
not in VNC_PORTS[
'EXCLUDE']:
277 raise CMException(
'vm_vnc_not_found')
279 log.debug(user.id,
"Found vnc port: %d" % new_vnc_port)
280 vm.vnc_port = new_vnc_port
283 used_ports = VM.objects.exclude(state__in=[vm_states[
'closed'], vm_states[
'erased']]).values_list(
'novnc_port', flat=
True)
284 for new_novnc_port
in xrange(NOVNC_PORTS[
'START'], NOVNC_PORTS[
'END'] + 1):
285 if new_novnc_port
not in used_ports
and new_novnc_port
not in NOVNC_PORTS[
'EXCLUDE']:
288 raise CMException(
'vm_novnc_not_found')
290 log.debug(user.id,
"Found novnc port: %d" % new_novnc_port)
291 vm.novnc_port = new_novnc_port
296 vm.vnc_passwd =
password_gen(13, chars=[
'letters',
'digits'], extra_chars=
'!@#$%^&*()')
298 vm.ssh_username = ssh_username
299 vm.user_data = user_data
302 if not reservation_id:
303 reservation_id = vm.id
305 vm.reservation_id = reservation_id
312 log.debug(user.id,
"Attaching disks")
314 if i == 0
and disk_list:
315 for disk_id
in disk_list:
316 log.debug(user.id,
'Attaching disks to first VM')
317 disk = StorageImage.get(user.id, disk_id)
319 raise CMException(
'image_attached')
320 while disk.disk_dev
in disk_devs:
322 disk_devs.append(disk.disk_dev)
326 log.debug(user.id,
"Attaching CD")
327 if i == 0
and iso_list:
328 for iso_id
in iso_list:
329 log.debug(user.id,
'Attaching iso to first VM')
331 iso = IsoImage.get(user.id, iso_id)
336 for i, vm
in enumerate(vms):
338 lease = Lease.objects.get(id=lease_id)
340 if lease.user_network.user != user:
341 raise CMException(
'lease_permission')
344 raise CMException(
'lease_attached')
346 log.debug(user.id,
"Attached ip: %s" % lease.address)
348 lease = AvailableNetwork.get_lease(user)
351 log.debug(user.id,
"Attached ip: %s" % lease.address)
353 if i == 0
and public_ip_id > 0:
354 log.debug(user.id,
"Attaching PublicIP")
356 publicip = PublicIP.objects.filter(user=user).
get(id=public_ip_id)
357 publicip.assign(lease)
360 log.exception(user.id, str(e))
361 raise CMException(
"lease_not_found")
374 conn = libvirt.open(self.node.conn_string)
375 storage = conn.storagePoolLookupByName(
'images')
377 path = storage.storageVolLookupByName(
'info').
path()
379 return os.path.join(os.path.dirname(path), str(self.id))
382 return bool(self.
farm)
and self == self.farm.head
385 return bool(self.
farm)
394 lv_template = loader.get_template(
"%s.xml" % self.node.driver)
395 c = Context({
'vm': self,
396 'uuid': uuid.uuid1(),
397 'memory': self.template.memory * 1024,
398 'cpu': self.template.cpu,
399 'image_path': self.
path
402 domain_template = lv_template.render(c)
404 log.debug(self.user.id, str(e))
405 return domain_template
415 django_settings.configure()
420 template = open(
"%s/%s-network.xml" % (settings.TEMPLATE_DIR, settings.NETWORK_TYPE)).read()
423 lv_template = loader.get_template_from_string(template)
424 c = Context({
'vm': self})
425 lv_template = lv_template.render(c)
427 log.debug(self.user.id, str(e))
445 vm = VM.objects.get(pk=vm_id)
447 raise CMException(
'vm_get')
449 if vm.user.id != user_id:
450 raise CMException(
'user_permission')
466 vm = VM.objects.get(pk=vm_id)
468 raise CMException(
'vm_get')
483 self.save(update_fields=[
'state'])
486 log.exception(self.user.id,
'save img')
490 img = SystemImage.create(name=(self.
name +
"_autosave" if self.
save_vm == 1
else self.
name),
491 description=self.
description, user=self.
user, platform=self.system_image.platform,
492 disk_controller=self.system_image.disk_controller,
493 network_device=self.system_image.network_device,
494 video_device=self.system_image.video_device)
495 img.size = self.system_image.size
499 img.copy_to_storage(self, img)
504 message.error(self.user.id,
'vm_save', {
'id': self.id,
'name': self.
name})
509 log.exception(self.user.id,
"Cannot commit changes: %s" % e)
510 log.exception(self.user.id,
"Cannot move image - error code: %s" % e)
513 img.state = image_states[
'ok']
517 log.error(self.user.id,
"Cannot commit changes: %s" % e)
518 message.error(self.user.id,
'vm_save', {
'id': self.id,
'name': self.
name})
521 message.info(self.user_id,
'farm_saved', {
'farm_name': self.vm.farm.name})
523 message.info(self.user_id,
'vm_saved', {
'vm_name': self.
name})
531 if not self.
state in (vm_states[
'closing'], vm_states[
'saving']):
536 log.exception(self.user.id,
'closing img')
541 conn = libvirt.open(self.node.conn_string)
542 pool = conn.storagePoolLookupByName(
'images')
544 vol = pool.storageVolLookupByName(str(self.id))
548 log.debug(self.user.id,
"Cannot remove image: %s" % str(e))
553 message.error(self.user.id,
'vm_destroy', {
'id': self.id,
'name': self.
name})
557 log.exception(self.user.id,
"Cannot commit changes: %s" % e)
567 log.debug(self.user.id,
"Detaching vnc for vm %d" % self.id)
572 log.debug(self.user.id, str(e))
576 log.debug(self.user.id,
"Detaching leases for vm %d" % self.id)
577 for lease
in self.lease_set.all():
578 log.debug(self.user.id,
"\t...detaching lease %s" % lease.address)
583 log.debug(self.user.id,
"Detaching disks for vm %d" % self.id)
594 log.exception(self.user_id,
"Cannot update resurce information: %s", str(e))
614 self = VM.objects.get(pk=self.id)
616 log.debug(self.user.id,
'Saving image')
618 log.debug(self.user.id,
'Removing image')
621 log.debug(self.user.id,
'Releasing resources')
629 self.farm.state = farm_states['closed']
635 log.exception(self.user.id,
"Cannot commit changes: %s" % e)
647 conn = libvirt.open(self.node.conn_string)
655 self.farm.state = farm_states[
'failed']
657 log.exception(self.user.id, str(e))
658 raise CMException(
'vm_get_lv_domain')
669 return self.storageimage_set.all()
683 img.disk_dev = 'sr%s' % chr(letter)
692 raise CMException(
'iso_image_attach')
708 states = {
'init': (),
709 'running': (
'init',
'turned off',
'restart',),
710 'running ctx': (
'running',
'running ctx',),
711 'closing': (
'turned off',
'running',
'running ctx',
'saving',
'turned off',),
712 'closed': (
'saving',
'closing',
'erased'),
713 'saving': (
'running',
'running ctx',),
714 'saving failed': (
'saving',),
715 'failed': (
'init',
'running',
'running ctx',
'closing',
'closed',
'saving',
'saving failed',
'failed',
716 'turned off',
'suspend',
'restart',
'erased'),
717 'turned off': (
'running',
'init',),
718 'suspend': (
'running',
'running ctx',),
719 'restart': (
'running',
'running ctx',),
721 'init',
'running',
'running ctx',
'closing',
'closed',
'saving',
'saving failed',
'failed',
722 'turned off',
'suspend',
'restart',
'erased',
'erasing'),
723 'erased': (
'erasing',
'erased')
728 for s
in vm_states.keys():
732 log.info(self.user.id,
"Changing state from %s to %s for %d" % (my_state, state, self.id))
735 if (my_state
not in states[state]
or my_state ==
False)
and my_state !=
'erasing':
736 raise CMException(
'vm_wrong_state',
'%s -> %s for %d' % (my_state, state, self.id))
738 self.
state = vm_states[state]
741 if state
in (
'failed',
'saving failed')
and my_state !=
'erasing':
760 vm.set_state(
'erasing')
765 log.error(vm.user.id,
'Cannot set save=0')
767 conn = libvirt.open(vm.node.conn_string)
769 domain = conn.lookupByID(vm.libvirt_id)
772 log.error(vm.user.id,
"Cannot find libvirt domain (by ID): %d (%s)" % (vm.libvirt_id, str(e)))
774 domain = conn.lookupByName(
"vm-%d-%d" % (vm.id, vm.user.id))
777 log.error(vm.user.id,
"Cannot find libvirt domain (by name): %d (%s)" % (vm.libvirt_id, str(e)))
780 pool = conn.storagePoolLookupByName(
'images')
782 vol = pool.storageVolLookupByName(str(vm.id))
785 log.error(vm.user.id,
"Cannot remove vm image (or other SSH error)")
788 vm.release_resources()
790 log.error(vm.user.id,
"Cannot release resources: %s" % str(e))
792 for l
in vm.lease_set.all():
796 log.error(vm.user.id,
"Cannot detach lease: %s" % str(e))
798 l.state = lease_states[
'free']
799 vm.state = vm_states[
'erased']
800 vm.stop_time = datetime.now()
805 log.error(vm.user.id,
"Cannot commit changes.")
810 res = [
'60',
'300',
'900']
812 return RrdHandler().
cpu_load(
'vm-%d-%d' % (self.id, self.user_id), res)
813 except CMException, e:
814 log.error(self.user_id,
'cpu_load: %s' % str(e))
815 return dict([(x,
'')
for x
in res])
826 vm = VM.objects.get(pk=vm.id)
827 log.debug(vm.user.id,
"Killing VM id: %s, state: %s" % (vm.id, vm.state))
829 if vm.state
in (vm_states[
'closing'], vm_states[
'saving']):
831 results.append({
'status':
'vm_already_closing',
'data':
''})
834 if vm.state
in (vm_states[
'erased'], vm_states[
'closed']):
836 results.append({
'status':
'vm_wrong_state',
'data':
''})
868 log.exception(vm.user.id,
'error destroying VM: %s' % str(e))
869 results.append({
'status':
'vm_destroy',
'data':
''})
870 message.error(vm.user_id,
'vm_destroy', {
'id': vm.id,
'name': vm.name})
874 results.append({
'status':
'ok',
'data':
''})
881 ip = str(IPNetwork(
'%s/30' % ip).network)
883 vm = VM.objects.filter(lease__address=ip)[0]
885 raise CMException(
'vm_get')
891 from cm.utils.threads.vm
import VMThread
895 if vm.state
in (vm_states[
'running'], vm_states[
'running ctx']):
896 thread = VMThread(vm,
'reset')
898 results.append({
'status':
'ok',
'data':
''})
900 results.append({
'status':
'vm_wrong_state',
'data':
''})
905 if not self.
state in (vm_states[
'init'], vm_states[
'running'], vm_states[
'running ctx']):
906 raise CMException(
'vm_wrong_state')
908 if self.
vnc_enabled == vnc_states[
'attached']
and reattach ==
False:
909 raise CMException(
'vm_vnc_attached')
912 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-A",
"CC1_VNC_REDIRECT",
"-d", settings.VNC_ADDRESS,
"-p",
"tcp",
913 "--dport", str(self.
vnc_port),
"-j",
"DNAT",
"--to-destination",
914 "%s:%s" % (self.node.address, str(self.
vnc_port))])
916 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-A",
"CC1_VNC_MASQUERADE",
"-d", self.node.address,
"-p",
"tcp",
917 "--dport", str(self.
vnc_port),
"-j",
"MASQUERADE"])
919 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-A",
"CC1_VNC_REDIRECT",
"-d", settings.VNC_ADDRESS,
"-p",
"tcp",
920 "--dport", str(self.
novnc_port),
"-j",
"DNAT",
"--to-destination",
921 "%s:%s" % (self.node.address, str(self.
novnc_port))])
923 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-A",
"CC1_VNC_MASQUERADE",
"-d", self.node.address,
"-p",
"tcp",
924 "--dport", str(self.
novnc_port),
"-j",
"MASQUERADE"])
930 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-D",
"CC1_VNC_REDIRECT",
"-d", settings.VNC_ADDRESS,
"-p",
"tcp",
931 "--dport", str(self.
vnc_port),
"-j",
"DNAT",
"--to-destination",
932 "%s:%s" % (self.node.address, str(self.
vnc_port))])
934 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-D",
"CC1_VNC_MASQUERADE",
"-d", self.node.address,
"-p",
"tcp",
935 "--dport", str(self.
vnc_port),
"-j",
"MASQUERADE"])
938 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-D",
"CC1_VNC_REDIRECT",
"-d", settings.VNC_ADDRESS,
"-p",
"tcp",
939 "--dport", str(self.
novnc_port),
"-j",
"DNAT",
"--to-destination",
940 "%s:%s" % (self.node.address, str(self.
novnc_port))])
942 [
"sudo",
"/sbin/iptables",
"-t",
"nat",
"-D",
"CC1_VNC_MASQUERADE",
"-d", self.node.address,
"-p",
"tcp",
943 "--dport", str(self.
novnc_port),
"-j",
"MASQUERADE"])
949 vm.description = description
955 raise CMException(
'vm_save')
957 if vm.state != vm_states[
'running ctx']:
958 raise CMException(
'vm_cannot_shutdown')
961 from cm.models.command
import Command
963 Command.execute(
'shutdown', user_id, vm.id)
965 raise CMException(
'vm_ctx_connect')