ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 2696:59daee42dceb

bitkeeper revision 1.1159.123.3 (4177d3a9BNL-5fZwDM69tw62fkzu4A)

Merge freefall.cl.cam.ac.uk:/auto/groups/xeno/users/cl349/BK/xeno.bk-26dom0
into freefall.cl.cam.ac.uk:/local/scratch/cl349/xeno.bk-24dom0
author cl349@freefall.cl.cam.ac.uk
date Thu Oct 21 15:20:09 2004 +0000 (2004-10-21)
parents 2584528df9e1 5576a0159e82
children 18ec571f3b9e 3897c6ce9024 41e31d1ac03f
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
3 """Representation of a single domain.
4 Includes support for domain construction, using
5 open-ended configurations.
7 Author: Mike Wray <mike.wray@hpl.hp.com>
9 """
11 import string
12 import types
13 import re
14 import sys
15 import os
16 import time
18 from twisted.internet import defer
20 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
21 import xen.util.ip
22 from xen.util.ip import _readline, _readlines
24 import sxp
26 import XendConsole
27 xendConsole = XendConsole.instance()
28 from XendLogging import log
29 from XendRoot import get_component
31 import server.SrvDaemon
32 xend = server.SrvDaemon.instance()
34 from XendError import VmError
36 from server.blkif import blkdev_name_to_number
38 """The length of domain names that Xen can handle.
39 The names stored in Xen itself are not used for much, and
40 xend can handle domain names of any length.
41 """
42 MAX_DOMAIN_NAME = 15
44 """Flag for a block device backend domain."""
45 SIF_BLK_BE_DOMAIN = (1<<4)
47 """Flag for a net device backend domain."""
48 SIF_NET_BE_DOMAIN = (1<<5)
50 """Shutdown code for poweroff."""
51 DOMAIN_POWEROFF = 0
52 """Shutdown code for reboot."""
53 DOMAIN_REBOOT = 1
54 """Shutdown code for suspend."""
55 DOMAIN_SUSPEND = 2
57 """Map shutdown codes to strings."""
58 shutdown_reasons = {
59 DOMAIN_POWEROFF: "poweroff",
60 DOMAIN_REBOOT : "reboot",
61 DOMAIN_SUSPEND : "suspend" }
63 RESTART_ALWAYS = 'always'
64 RESTART_ONREBOOT = 'onreboot'
65 RESTART_NEVER = 'never'
67 restart_modes = [
68 RESTART_ALWAYS,
69 RESTART_ONREBOOT,
70 RESTART_NEVER,
71 ]
73 STATE_RESTART_PENDING = 'pending'
74 STATE_RESTART_BOOTING = 'booting'
76 STATE_VM_OK = "ok"
77 STATE_VM_TERMINATED = "terminated"
80 def domain_exists(name):
81 # See comment in XendDomain constructor.
82 xd = get_component('xen.xend.XendDomain')
83 return xd.domain_exists(name)
85 def shutdown_reason(code):
86 """Get a shutdown reason from a code.
88 @param code: shutdown code
89 @type code: int
90 @return: shutdown reason
91 @rtype: string
92 """
93 return shutdown_reasons.get(code, "?")
95 def make_disk(vm, config, uname, dev, mode, recreate=0):
96 """Create a virtual disk device for a domain.
98 @param vm: vm
99 @param uname: device to export
100 @param dev: device name in domain
101 @param mode: read/write mode
102 @param recreate: recreate flag (after xend restart)
103 @return: deferred
104 """
105 idx = vm.next_device_index('vbd')
106 # todo: The 'dev' should be looked up in the context of the domain.
107 vdev = blkdev_name_to_number(dev)
108 if not vdev:
109 raise VmError("vbd: Device not found: uname=%s dev=%s" % (uname, dev))
110 ctrl = xend.blkif_create(vm.dom, recreate=recreate)
111 return ctrl.attachDevice(idx, config, uname, vdev, mode, recreate=recreate)
113 def vif_up(iplist):
114 """send an unsolicited ARP reply for all non link-local IP addresses.
116 @param iplist: IP addresses
117 """
119 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
121 def get_ip_nonlocal_bind():
122 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
124 def set_ip_nonlocal_bind(v):
125 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
127 def link_local(ip):
128 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
130 def arping(ip, gw):
131 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
132 log.debug(cmd)
133 os.system(cmd)
135 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
136 nlb = get_ip_nonlocal_bind()
137 if not nlb: set_ip_nonlocal_bind(1)
138 try:
139 for ip in iplist:
140 if not link_local(ip):
141 arping(ip, gateway)
142 finally:
143 if not nlb: set_ip_nonlocal_bind(0)
145 config_handlers = {}
147 def add_config_handler(name, h):
148 """Add a handler for a config field.
150 @param name: field name
151 @param h: handler: fn(vm, config, field, index)
152 """
153 config_handlers[name] = h
155 def get_config_handler(name):
156 """Get a handler for a config field.
158 returns handler or None
159 """
160 return config_handlers.get(name)
162 """Table of handlers for virtual machine images.
163 Indexed by image type.
164 """
165 image_handlers = {}
167 def add_image_handler(name, h):
168 """Add a handler for an image type
169 @param name: image type
170 @param h: handler: fn(config, name, memory, image)
171 """
172 image_handlers[name] = h
174 def get_image_handler(name):
175 """Get the handler for an image type.
176 @param name: image type
177 @return: handler or None
178 """
179 return image_handlers.get(name)
181 """Table of handlers for devices.
182 Indexed by device type.
183 """
184 device_handlers = {}
186 def add_device_handler(name, h):
187 """Add a handler for a device type.
189 @param name: device type
190 @param h: handler: fn(vm, dev)
191 """
192 device_handlers[name] = h
194 def get_device_handler(name):
195 """Get the handler for a device type.
197 @param name : device type
198 @return; handler or None
199 """
200 return device_handlers.get(name)
202 def vm_create(config):
203 """Create a VM from a configuration.
204 If a vm has been partially created and there is an error it
205 is destroyed.
207 @param config configuration
208 @return: Deferred
209 @raise: VmError for invalid configuration
210 """
211 vm = XendDomainInfo()
212 return vm.construct(config)
214 def vm_recreate(savedinfo, info):
215 """Create the VM object for an existing domain.
217 @param savedinfo: saved info from the domain DB
218 @type savedinfo: sxpr
219 @param info: domain info from xc
220 @type info: xc domain dict
221 @return: deferred
222 """
223 vm = XendDomainInfo()
224 vm.recreate = 1
225 vm.setdom(info['dom'])
226 #vm.name = info['name']
227 vm.memory = info['mem_kb']/1024
228 start_time = sxp.child_value(savedinfo, 'start_time')
229 if start_time is not None:
230 vm.start_time = float(start_time)
231 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
232 restart_time = sxp.child_value(savedinfo, 'restart_time')
233 if restart_time is not None:
234 vm.restart_time = float(restart_time)
235 config = sxp.child_value(savedinfo, 'config')
236 if config:
237 d = vm.construct(config)
238 else:
239 vm.name = sxp.child_value(savedinfo, 'name')
240 d = defer.succeed(vm)
241 vm.recreate = 0
242 return d
244 def vm_restore(src, progress=0):
245 """Restore a VM from a disk image.
247 src saved state to restore
248 progress progress reporting flag
249 returns deferred
250 raises VmError for invalid configuration
251 """
252 vm = XendDomainInfo()
253 ostype = "linux" #todo Set from somewhere (store in the src?).
254 restorefn = getattr(xc, "%s_restore" % ostype)
255 d = restorefn(state_file=src, progress=progress)
256 dom = int(d['dom'])
257 if dom < 0:
258 raise VmError('restore failed')
259 try:
260 vmconfig = sxp.from_string(d['vmconfig'])
261 config = sxp.child_value(vmconfig, 'config')
262 except Exception, ex:
263 raise VmError('config error: ' + str(ex))
264 deferred = vm.dom_construct(dom, config)
265 def vifs_cb(val, vm):
266 vif_up(vm.ipaddrs)
267 return vm
268 deferred.addCallback(vifs_cb, vm)
269 return deferred
271 def dom_get(dom):
272 """Get info from xen for an existing domain.
274 @param dom: domain id
275 @return: info or None
276 """
277 domlist = xc.domain_getinfo(dom, 1)
278 if domlist and dom == domlist[0]['dom']:
279 return domlist[0]
280 return None
282 def append_deferred(dlist, v):
283 """Append a value to a deferred list if it is a deferred.
285 @param dlist: list of deferreds
286 @param v: value to add
287 """
288 if isinstance(v, defer.Deferred):
289 dlist.append(v)
291 class XendDomainInfo:
292 """Virtual machine object."""
294 """Minimum time between domain restarts in seconds.
295 """
296 MINIMUM_RESTART_TIME = 20
298 def __init__(self):
299 self.recreate = 0
300 self.restore = 0
301 self.config = None
302 self.id = None
303 self.dom = None
304 self.cpu_weight = 1
305 self.start_time = None
306 self.name = None
307 self.memory = None
308 self.image = None
309 self.ramdisk = None
310 self.cmdline = None
311 self.console = None
312 self.devices = {}
313 self.device_index = {}
314 self.configs = []
315 self.info = None
316 self.ipaddrs = []
317 self.blkif_backend = 0
318 self.netif_backend = 0
319 #todo: state: running, suspended
320 self.state = STATE_VM_OK
321 #todo: set to migrate info if migrating
322 self.migrate = None
323 self.restart_mode = RESTART_ONREBOOT
324 self.restart_state = None
325 self.restart_time = None
326 self.console_port = None
328 def setdom(self, dom):
329 """Set the domain id.
331 @param dom: domain id
332 """
333 self.dom = int(dom)
334 self.id = str(dom)
336 def update(self, info):
337 """Update with info from xc.domain_getinfo().
338 """
339 self.info = info
340 self.memory = self.info['mem_kb'] / 1024
342 def __str__(self):
343 s = "domain"
344 s += " id=" + self.id
345 s += " name=" + self.name
346 s += " memory=" + str(self.memory)
347 if self.console:
348 s += " console=" + str(self.console.console_port)
349 if self.image:
350 s += " image=" + self.image
351 s += ""
352 return s
354 __repr__ = __str__
356 def sxpr(self):
357 sxpr = ['domain',
358 ['id', self.id],
359 ['name', self.name],
360 ['memory', self.memory] ]
362 if self.info:
363 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
364 run = (self.info['running'] and 'r') or '-'
365 block = (self.info['blocked'] and 'b') or '-'
366 pause = (self.info['paused'] and 'p') or '-'
367 shut = (self.info['shutdown'] and 's') or '-'
368 crash = (self.info['crashed'] and 'c') or '-'
369 state = run + block + pause + shut + crash
370 sxpr.append(['state', state])
371 if self.info['shutdown']:
372 reason = shutdown_reason(self.info['shutdown_reason'])
373 sxpr.append(['shutdown_reason', reason])
374 sxpr.append(['cpu', self.info['cpu']])
375 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
377 if self.start_time:
378 up_time = time.time() - self.start_time
379 sxpr.append(['up_time', str(up_time) ])
380 sxpr.append(['start_time', str(self.start_time) ])
382 if self.console:
383 sxpr.append(self.console.sxpr())
384 if self.restart_state:
385 sxpr.append(['restart_state', self.restart_state])
386 if self.restart_time:
387 sxpr.append(['restart_time', str(self.restart_time)])
388 if self.config:
389 sxpr.append(['config', self.config])
390 return sxpr
392 def check_name(self, name):
393 """Check if a vm name is valid. Valid names start with a non-digit
394 and contain alphabetic characters, digits, or characters in '_-.:/+'.
395 The same name cannot be used for more than one vm at the same time.
397 @param name: name
398 @raise: VMerror if invalid
399 """
400 if self.recreate: return
401 if name is None or name == '':
402 raise VmError('missing vm name')
403 if name[0] in string.digits:
404 raise VmError('invalid vm name')
405 for c in name:
406 if c in string.digits: continue
407 if c in '_-.:/+': continue
408 if c in string.ascii_letters: continue
409 raise VmError('invalid vm name')
410 dominfo = domain_exists(name)
411 # When creating or rebooting, a domain with my name should not exist.
412 # When restoring, a domain with my name will exist, but it should have
413 # my domain id.
414 if not dominfo:
415 return
416 if dominfo.is_terminated():
417 return
418 if not self.dom or (dominfo.dom != self.dom):
419 raise VmError('vm name clash: ' + name)
421 def construct(self, config):
422 """Construct the vm instance from its configuration.
424 @param config: configuration
425 @return: deferred
426 @raise: VmError on error
427 """
428 # todo - add support for scheduling params?
429 self.config = config
430 try:
431 self.name = sxp.child_value(config, 'name')
432 self.check_name(self.name)
433 try:
434 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
435 except:
436 raise VmError('invalid cpu weight')
437 self.memory = int(sxp.child_value(config, 'memory'))
438 if self.memory is None:
439 raise VmError('missing memory size')
440 cpu = sxp.child_value(config, 'cpu')
441 if self.dom and cpu is not None:
442 xc.domain_pincpu(self.dom, int(cpu))
444 self.init_domain()
445 self.configure_console()
446 self.construct_image()
447 self.configure_restart()
448 self.configure_backends()
449 deferred = self.configure()
450 def cberr(err):
451 self.destroy()
452 return err
453 deferred.addErrback(cberr)
454 except StandardError, ex:
455 # Catch errors, cleanup and re-raise.
456 self.destroy()
457 raise
458 return deferred
460 def construct_image(self):
461 """Construct the boot image for the domain.
463 @return vm
464 """
465 image = sxp.child_value(self.config, 'image')
466 if image is None:
467 raise VmError('missing image')
468 image_name = sxp.name(image)
469 if image_name is None:
470 raise VmError('missing image name')
471 image_handler = get_image_handler(image_name)
472 if image_handler is None:
473 raise VmError('unknown image type: ' + image_name)
474 image_handler(self, image)
475 return self
477 def config_devices(self, name):
478 """Get a list of the 'device' nodes of a given type from the config.
480 @param name: device type
481 @type name: string
482 @return: device configs
483 @rtype: list
484 """
485 devices = []
486 for d in sxp.children(self.config, 'device'):
487 dev = sxp.child0(d)
488 if dev is None: continue
489 if name == sxp.name(dev):
490 devices.append(dev)
491 return devices
493 def config_device(self, type, idx):
494 """Get a device config from the device nodes of a given type
495 from the config.
497 @param type: device type
498 @type type: string
499 @param idx: index
500 @type idx: int
501 @return config or None
502 """
503 devs = self.config_devices(type)
504 if 0 <= idx < len(devs):
505 return devs[idx]
506 else:
507 return None
509 def next_device_index(self, type):
510 """Get the next index for a given device type.
512 @param type: device type
513 @type type: string
514 @return device index
515 @rtype: int
516 """
517 idx = self.device_index.get(type, 0)
518 self.device_index[type] = idx + 1
519 return idx
521 def add_device(self, type, dev):
522 """Add a device to a virtual machine.
524 @param type: device type
525 @param dev: device to add
526 """
527 dl = self.devices.get(type, [])
528 dl.append(dev)
529 self.devices[type] = dl
531 def remove_device(self, type, dev):
532 """Remove a device from a virtual machine.
534 @param type: device type
535 @param dev: device
536 """
537 dl = self.devices.get(type, [])
538 if dev in dl:
539 dl.remove(dev)
541 def get_devices(self, type):
542 """Get a list of the devices of a given type.
544 @param type: device type
545 @return: devices
546 """
547 val = self.devices.get(type, [])
548 return val
550 def get_device_by_id(self, type, id):
551 """Get the device with the given id.
553 @param id: device id
554 @return: device or None
555 """
556 dl = self.get_devices(type)
557 for d in dl:
558 if d.getprop('id') == id:
559 return d
560 return None
562 def get_device_by_index(self, type, idx):
563 """Get the device with the given index.
565 @param idx: device index
566 @return: device or None
567 """
568 idx = str(idx)
569 dl = self.get_devices(type)
570 for d in dl:
571 if d.getidx() == idx:
572 return d
573 return None
575 def add_config(self, val):
576 """Add configuration data to a virtual machine.
578 @param val: data to add
579 """
580 self.configs.append(val)
582 def destroy(self):
583 """Completely destroy the vm.
584 """
585 self.cleanup()
586 return self.destroy_domain()
588 def destroy_domain(self):
589 """Destroy the vm's domain.
590 The domain will not finally go away unless all vm
591 devices have been released.
592 """
593 if self.dom is None: return 0
594 self.destroy_console()
595 chan = xend.getDomChannel(self.dom)
596 if chan:
597 log.debug("Closing channel to domain %d", self.dom)
598 chan.close()
599 try:
600 return xc.domain_destroy(dom=self.dom)
601 except Exception, err:
602 log.exception("Domain destroy failed: %s", self.name)
604 def destroy_console(self):
605 if self.console:
606 if self.restart_pending():
607 self.console.deregisterChannel()
608 else:
609 log.debug('Closing console, domain %s', self.id)
610 self.console.close()
612 def cleanup(self):
613 """Cleanup vm resources: release devices.
614 """
615 self.state = STATE_VM_TERMINATED
616 self.release_devices()
618 def is_terminated(self):
619 """Check if a domain has been terminated.
620 """
621 return self.state == STATE_VM_TERMINATED
623 def release_devices(self):
624 """Release all vm devices.
625 """
626 self.release_vifs()
627 self.release_vbds()
629 self.devices = {}
630 self.device_index = {}
631 self.configs = []
632 self.ipaddrs = []
634 def release_vifs(self):
635 """Release vm virtual network devices (vifs).
636 """
637 if self.dom is None: return
638 ctrl = xend.netif_get(self.dom)
639 if ctrl:
640 log.debug("Destroying vifs for domain %d", self.dom)
641 ctrl.destroy()
643 def release_vbds(self):
644 """Release vm virtual block devices (vbds).
645 """
646 if self.dom is None: return
647 ctrl = xend.blkif_get(self.dom)
648 if ctrl:
649 log.debug("Destroying vbds for domain %d", self.dom)
650 ctrl.destroy()
652 def show(self):
653 """Print virtual machine info.
654 """
655 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
656 print "image:"
657 sxp.show(self.image)
658 print
659 for dl in self.devices:
660 for dev in dl:
661 print "device:"
662 sxp.show(dev)
663 print
664 for val in self.configs:
665 print "config:"
666 sxp.show(val)
667 print
668 print "]"
670 def init_domain(self):
671 """Initialize the domain memory.
672 """
673 if self.recreate:
674 return
675 if self.start_time is None:
676 self.start_time = time.time()
677 if self.restore:
678 return
679 dom = self.dom or 0
680 memory = self.memory
681 name = self.name
682 # If the name is over the xen limit, use the end of it.
683 if len(name) > MAX_DOMAIN_NAME:
684 name = name[-MAX_DOMAIN_NAME:]
685 try:
686 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
687 except:
688 raise VmError('invalid cpu')
689 cpu_weight = self.cpu_weight
690 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024,
691 cpu= cpu, cpu_weight= cpu_weight)
692 if dom <= 0:
693 raise VmError('Creating domain failed: name=%s memory=%d'
694 % (name, memory))
695 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
696 self.setdom(dom)
698 def build_domain(self, ostype, kernel, ramdisk, cmdline):
699 """Build the domain boot image.
700 """
701 if self.recreate or self.restore: return
702 if len(cmdline) >= 256:
703 log.warning('kernel cmdline too long, domain %d', self.dom)
704 dom = self.dom
705 buildfn = getattr(xc, '%s_build' % ostype)
706 flags = 0
707 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
708 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
709 err = buildfn(dom = dom,
710 image = kernel,
711 control_evtchn = self.console.getRemotePort(),
712 cmdline = cmdline,
713 ramdisk = ramdisk,
714 flags = flags)
715 if err != 0:
716 raise VmError('Building domain failed: type=%s dom=%d err=%d'
717 % (ostype, dom, err))
719 def create_domain(self, ostype, kernel, ramdisk, cmdline):
720 """Create a domain. Builds the image but does not configure it.
722 @param ostype: OS type
723 @param kernel: kernel image
724 @param ramdisk: kernel ramdisk
725 @param cmdline: kernel commandline
726 """
727 if not self.recreate:
728 if not os.path.isfile(kernel):
729 raise VmError('Kernel image does not exist: %s' % kernel)
730 if ramdisk and not os.path.isfile(ramdisk):
731 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
732 #self.init_domain()
733 if self.console:
734 self.console.registerChannel()
735 else:
736 self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
737 self.build_domain(ostype, kernel, ramdisk, cmdline)
738 self.image = kernel
739 self.ramdisk = ramdisk
740 self.cmdline = cmdline
742 def create_devices(self):
743 """Create the devices for a vm.
745 @return: Deferred
746 @raise: VmError for invalid devices
747 """
748 dlist = []
749 devices = sxp.children(self.config, 'device')
750 index = {}
751 for d in devices:
752 dev = sxp.child0(d)
753 if dev is None:
754 raise VmError('invalid device')
755 dev_name = sxp.name(dev)
756 dev_index = index.get(dev_name, 0)
757 dev_handler = get_device_handler(dev_name)
758 if dev_handler is None:
759 raise VmError('unknown device type: ' + dev_name)
760 v = dev_handler(self, dev, dev_index)
761 append_deferred(dlist, v)
762 index[dev_name] = dev_index + 1
763 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
764 return deferred
766 def device_create(self, dev_config):
767 """Create a new device.
769 @param dev_config: device configuration
770 @return: deferred
771 """
772 dev_name = sxp.name(dev_config)
773 dev_handler = get_device_handler(dev_name)
774 if dev_handler is None:
775 raise VmError('unknown device type: ' + dev_name)
776 devs = self.get_devices(dev_name)
777 dev_index = len(devs)
778 self.config.append(['device', dev_config])
779 d = dev_handler(self, dev_config, dev_index, change=1)
780 def cbok(dev):
781 return dev.sxpr()
782 d.addCallback(cbok)
783 return d
785 def device_configure(self, dev_config, idx):
786 """Configure an existing device.
788 @param dev_config: device configuration
789 @param idx: device index
790 """
791 type = sxp.name(dev_config)
792 dev = self.get_device_by_index(type, idx)
793 if not dev:
794 raise VmError('invalid device: %s %s' % (type, idx))
795 new_config = dev.configure(dev_config, change=1)
796 devs = self.devices.get(type)
797 index = devs.index(dev)
798 # Patch new config into device configs.
799 dev_configs = self.config_devices(type)
800 old_config = dev_configs[index]
801 dev_configs[index] = new_config
802 # Patch new config into vm config.
803 new_full_config = ['device', new_config]
804 old_full_config = ['device', old_config]
805 old_index = self.config.index(old_full_config)
806 self.config[old_index] = new_full_config
807 return new_config
809 def device_destroy(self, type, idx):
810 """Destroy a device.
812 @param type: device type
813 @param idx: device index
814 """
815 dev = self.get_device_by_index(type, idx)
816 if not dev:
817 raise VmError('invalid device: %s %s' % (type, idx))
818 devs = self.devices.get(type)
819 index = devs.index(dev)
820 dev_config = self.config_device(type, index)
821 if dev_config:
822 self.config.remove(['device', dev_config])
823 dev.destroy(change=1)
824 self.remove_device(type, dev)
826 def configure_memory(self):
827 """Configure vm memory limit.
828 """
829 maxmem = sxp.get_child_value(self.config, "maxmem")
830 if maxmem is None:
831 maxmem = self.memory
832 xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
834 def configure_console(self):
835 """Configure the vm console port.
836 """
837 x = sxp.child_value(self.config, 'console')
838 if x:
839 try:
840 port = int(x)
841 except:
842 raise VmError('invalid console:' + str(x))
843 self.console_port = port
845 def configure_restart(self):
846 """Configure the vm restart mode.
847 """
848 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
849 if r not in restart_modes:
850 raise VmError('invalid restart mode: ' + str(r))
851 self.restart_mode = r;
853 def restart_needed(self, reason):
854 """Determine if the vm needs to be restarted when shutdown
855 for the given reason.
857 @param reason: shutdown reason
858 @return 1 if needs restaert, 0 otherwise
859 """
860 if self.restart_mode == RESTART_NEVER:
861 return 0
862 if self.restart_mode == RESTART_ALWAYS:
863 return 1
864 if self.restart_mode == RESTART_ONREBOOT:
865 return reason == 'reboot'
866 return 0
868 def restart_cancel(self):
869 """Cancel a vm restart.
870 """
871 self.restart_state = None
873 def restarting(self):
874 """Put the vm into restart mode.
875 """
876 self.restart_state = STATE_RESTART_PENDING
878 def restart_pending(self):
879 """Test if the vm has a pending restart.
880 """
881 return self.restart_state == STATE_RESTART_PENDING
883 def restart_check(self):
884 """Check if domain restart is OK.
885 To prevent restart loops, raise an error if it is
886 less than MINIMUM_RESTART_TIME seconds since the last restart.
887 """
888 tnow = time.time()
889 if self.restart_time is not None:
890 tdelta = tnow - self.restart_time
891 if tdelta < self.MINIMUM_RESTART_TIME:
892 self.restart_cancel()
893 msg = 'VM %s restarting too fast' % self.name
894 log.error(msg)
895 raise VmError(msg)
896 self.restart_time = tnow
898 def restart(self):
899 """Restart the domain after it has exited.
900 Reuses the domain id and console port.
902 @return: deferred
903 """
904 try:
905 self.restart_check()
906 self.restart_state = STATE_RESTART_BOOTING
907 d = self.construct(self.config)
908 finally:
909 self.restart_state = None
910 return d
912 def configure_backends(self):
913 """Set configuration flags if the vm is a backend for netif or blkif.
914 Configure the backends to use for vbd and vif if specified.
915 """
916 for c in sxp.children(self.config, 'backend'):
917 v = sxp.child0(c)
918 name = sxp.name(v)
919 if name == 'blkif':
920 self.blkif_backend = 1
921 elif name == 'netif':
922 self.netif_backend = 1
923 else:
924 raise VmError('invalid backend type:' + str(name))
926 def configure(self):
927 """Configure a vm.
929 @return: deferred - calls callback with vm
930 """
931 d = self.create_blkif()
932 d.addCallback(lambda x: self.create_devices())
933 d.addCallback(self._configure)
934 return d
936 def _configure(self, val):
937 d = self.configure_fields()
938 def cbok(results):
939 return self
940 def cberr(err):
941 self.destroy()
942 return err
943 d.addCallback(cbok)
944 d.addErrback(cberr)
945 return d
947 def create_blkif(self):
948 """Create the block device interface (blkif) for the vm.
949 The vm needs a blkif even if it doesn't have any disks
950 at creation time, for example when it uses NFS root.
952 @return: deferred
953 """
954 ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
955 back = ctrl.getBackendInterface(0)
956 return back.connect(recreate=self.recreate)
958 def dom_construct(self, dom, config):
959 """Construct a vm for an existing domain.
961 @param dom: domain id
962 @param config: domain configuration
963 @return: deferred
964 """
965 d = dom_get(dom)
966 if not d:
967 raise VmError("Domain not found: %d" % dom)
968 try:
969 self.restore = 1
970 self.setdom(dom)
971 #self.name = d['name']
972 self.memory = d['mem_kb']/1024
973 deferred = self.construct(config)
974 finally:
975 self.restore = 0
976 return deferred
978 def configure_fields(self):
979 """Process the vm configuration fields using the registered handlers.
980 """
981 dlist = []
982 index = {}
983 for field in sxp.children(self.config):
984 field_name = sxp.name(field)
985 field_index = index.get(field_name, 0)
986 field_handler = get_config_handler(field_name)
987 # Ignore unknown fields. Warn?
988 if field_handler:
989 v = field_handler(self, self.config, field, field_index)
990 append_deferred(dlist, v)
991 else:
992 log.warning("Unknown config field %s", field_name)
993 index[field_name] = field_index + 1
994 d = defer.DeferredList(dlist, fireOnOneErrback=1)
995 return d
998 def vm_image_linux(vm, image):
999 """Create a VM for a linux image.
1001 @param name: vm name
1002 @param memory: vm memory
1003 @param image: image config
1004 @return: vm
1005 """
1006 kernel = sxp.child_value(image, "kernel")
1007 cmdline = ""
1008 ip = sxp.child_value(image, "ip", "dhcp")
1009 if ip:
1010 cmdline += " ip=" + ip
1011 root = sxp.child_value(image, "root")
1012 if root:
1013 cmdline += " root=" + root
1014 args = sxp.child_value(image, "args")
1015 if args:
1016 cmdline += " " + args
1017 ramdisk = sxp.child_value(image, "ramdisk", '')
1018 vm.create_domain("linux", kernel, ramdisk, cmdline)
1019 return vm
1021 def vm_dev_vif(vm, val, index, change=0):
1022 """Create a virtual network interface (vif).
1024 @param vm: virtual machine
1025 @param val: vif config
1026 @param index: vif index
1027 @return: deferred
1028 """
1029 vif = vm.next_device_index('vif')
1030 vmac = sxp.child_value(val, "mac")
1031 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1032 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1033 defer = ctrl.attachDevice(vif, val, recreate=vm.recreate)
1034 def cbok(dev):
1035 dev.vifctl('up', vmname=vm.name)
1036 vm.add_device('vif', dev)
1037 if change:
1038 dev.interfaceChanged()
1039 return dev
1040 defer.addCallback(cbok)
1041 return defer
1043 def vm_dev_vbd(vm, val, index, change=0):
1044 """Create a virtual block device (vbd).
1046 @param vm: virtual machine
1047 @param val: vbd config
1048 @param index: vbd index
1049 @return: deferred
1050 """
1051 uname = sxp.child_value(val, 'uname')
1052 if not uname:
1053 raise VmError('vbd: Missing uname')
1054 dev = sxp.child_value(val, 'dev')
1055 if not dev:
1056 raise VmError('vbd: Missing dev')
1057 mode = sxp.child_value(val, 'mode', 'r')
1058 log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
1059 defer = make_disk(vm, val, uname, dev, mode, vm.recreate)
1060 def fn(vbd):
1061 vbd.dev = dev
1062 vbd.uname = uname
1063 vm.add_device('vbd', vbd)
1064 if change:
1065 vbd.interfaceChanged()
1066 return vbd
1067 defer.addCallback(fn)
1068 return defer
1070 def parse_pci(val):
1071 """Parse a pci field.
1072 """
1073 if isinstance(val, types.StringType):
1074 radix = 10
1075 if val.startswith('0x') or val.startswith('0X'):
1076 radix = 16
1077 v = int(val, radix)
1078 else:
1079 v = val
1080 return v
1082 def vm_dev_pci(vm, val, index, change=0):
1083 """Add a pci device.
1085 @param vm: virtual machine
1086 @param val: device configuration
1087 @param index: device index
1088 @return: 0 on success
1089 """
1090 bus = sxp.child_value(val, 'bus')
1091 if not bus:
1092 raise VmError('pci: Missing bus')
1093 dev = sxp.child_value(val, 'dev')
1094 if not dev:
1095 raise VmError('pci: Missing dev')
1096 func = sxp.child_value(val, 'func')
1097 if not func:
1098 raise VmError('pci: Missing func')
1099 try:
1100 bus = parse_pci(bus)
1101 dev = parse_pci(dev)
1102 func = parse_pci(func)
1103 except:
1104 raise VmError('pci: invalid parameter')
1105 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1106 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1107 func=func, enable=1)
1108 if rc < 0:
1109 #todo non-fatal
1110 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1111 (bus, dev, func))
1112 return rc
1115 def vm_field_ignore(vm, config, val, index):
1116 """Dummy config field handler used for fields with built-in handling.
1118 @param vm: virtual machine
1119 @param config: vm config
1120 @param val: config field
1121 @param index: field index
1122 """
1123 pass
1125 def vm_field_maxmem(vm, config, val, index):
1126 """Configure vm memory limit.
1128 @param vm: virtual machine
1129 @param config: vm config
1130 @param val: config field
1131 @param index: field index
1132 """
1133 maxmem = sxp.child0(val)
1134 if maxmem is None:
1135 maxmem = vm.memory
1136 try:
1137 maxmem = int(maxmem)
1138 except:
1139 raise VmError("invalid maxmem: " + str(maxmem))
1140 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1142 # Register image handlers.
1143 add_image_handler('linux', vm_image_linux)
1145 # Register device handlers.
1146 add_device_handler('vif', vm_dev_vif)
1147 add_device_handler('vbd', vm_dev_vbd)
1148 add_device_handler('pci', vm_dev_pci)
1150 # Ignore the fields we already handle.
1151 add_config_handler('name', vm_field_ignore)
1152 add_config_handler('memory', vm_field_ignore)
1153 add_config_handler('cpu', vm_field_ignore)
1154 add_config_handler('cpu_weight', vm_field_ignore)
1155 add_config_handler('console', vm_field_ignore)
1156 add_config_handler('image', vm_field_ignore)
1157 add_config_handler('device', vm_field_ignore)
1158 add_config_handler('backend', vm_field_ignore)
1160 # Register other config handlers.
1161 add_config_handler('maxmem', vm_field_maxmem)