ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 3125:7827d8db3729

bitkeeper revision 1.1159.194.4 (41a5e757kS3W0lQ0UXkviQYcgBr7Gg)

XendDomainInfo.py:
Fix check for no configured block devices.
author cl349@arcadians.cl.cam.ac.uk
date Thu Nov 25 14:08:23 2004 +0000 (2004-11-25)
parents 6a8849c4b647
children adbc30fc0f2e
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 """The length of domain names that Xen can handle.
37 The names stored in Xen itself are not used for much, and
38 xend can handle domain names of any length.
39 """
40 MAX_DOMAIN_NAME = 15
42 """Flag for a block device backend domain."""
43 SIF_BLK_BE_DOMAIN = (1<<4)
45 """Flag for a net device backend domain."""
46 SIF_NET_BE_DOMAIN = (1<<5)
48 """Shutdown code for poweroff."""
49 DOMAIN_POWEROFF = 0
50 """Shutdown code for reboot."""
51 DOMAIN_REBOOT = 1
52 """Shutdown code for suspend."""
53 DOMAIN_SUSPEND = 2
55 """Map shutdown codes to strings."""
56 shutdown_reasons = {
57 DOMAIN_POWEROFF: "poweroff",
58 DOMAIN_REBOOT : "reboot",
59 DOMAIN_SUSPEND : "suspend" }
61 RESTART_ALWAYS = 'always'
62 RESTART_ONREBOOT = 'onreboot'
63 RESTART_NEVER = 'never'
65 restart_modes = [
66 RESTART_ALWAYS,
67 RESTART_ONREBOOT,
68 RESTART_NEVER,
69 ]
71 STATE_RESTART_PENDING = 'pending'
72 STATE_RESTART_BOOTING = 'booting'
74 STATE_VM_OK = "ok"
75 STATE_VM_TERMINATED = "terminated"
78 def domain_exists(name):
79 # See comment in XendDomain constructor.
80 xd = get_component('xen.xend.XendDomain')
81 return xd.domain_exists(name)
83 def shutdown_reason(code):
84 """Get a shutdown reason from a code.
86 @param code: shutdown code
87 @type code: int
88 @return: shutdown reason
89 @rtype: string
90 """
91 return shutdown_reasons.get(code, "?")
93 def vif_up(iplist):
94 """send an unsolicited ARP reply for all non link-local IP addresses.
96 @param iplist: IP addresses
97 """
99 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
101 def get_ip_nonlocal_bind():
102 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
104 def set_ip_nonlocal_bind(v):
105 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
107 def link_local(ip):
108 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
110 def arping(ip, gw):
111 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
112 log.debug(cmd)
113 os.system(cmd)
115 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
116 nlb = get_ip_nonlocal_bind()
117 if not nlb: set_ip_nonlocal_bind(1)
118 try:
119 for ip in iplist:
120 if not link_local(ip):
121 arping(ip, gateway)
122 finally:
123 if not nlb: set_ip_nonlocal_bind(0)
125 config_handlers = {}
127 def add_config_handler(name, h):
128 """Add a handler for a config field.
130 @param name: field name
131 @param h: handler: fn(vm, config, field, index)
132 """
133 config_handlers[name] = h
135 def get_config_handler(name):
136 """Get a handler for a config field.
138 returns handler or None
139 """
140 return config_handlers.get(name)
142 """Table of handlers for virtual machine images.
143 Indexed by image type.
144 """
145 image_handlers = {}
147 def add_image_handler(name, h):
148 """Add a handler for an image type
149 @param name: image type
150 @param h: handler: fn(config, name, memory, image)
151 """
152 image_handlers[name] = h
154 def get_image_handler(name):
155 """Get the handler for an image type.
156 @param name: image type
157 @return: handler or None
158 """
159 return image_handlers.get(name)
161 """Table of handlers for devices.
162 Indexed by device type.
163 """
164 device_handlers = {}
166 def add_device_handler(name, h):
167 """Add a handler for a device type.
169 @param name: device type
170 @param h: handler: fn(vm, dev)
171 """
172 device_handlers[name] = h
174 def get_device_handler(name):
175 """Get the handler for a device type.
177 @param name : device type
178 @return; handler or None
179 """
180 return device_handlers.get(name)
182 def vm_create(config):
183 """Create a VM from a configuration.
184 If a vm has been partially created and there is an error it
185 is destroyed.
187 @param config configuration
188 @return: Deferred
189 @raise: VmError for invalid configuration
190 """
191 vm = XendDomainInfo()
192 return vm.construct(config)
194 def vm_recreate(savedinfo, info):
195 """Create the VM object for an existing domain.
197 @param savedinfo: saved info from the domain DB
198 @type savedinfo: sxpr
199 @param info: domain info from xc
200 @type info: xc domain dict
201 @return: deferred
202 """
203 vm = XendDomainInfo()
204 vm.recreate = 1
205 vm.savedinfo = savedinfo
206 vm.setdom(info['dom'])
207 #vm.name = info['name']
208 vm.memory = info['mem_kb']/1024
209 start_time = sxp.child_value(savedinfo, 'start_time')
210 if start_time is not None:
211 vm.start_time = float(start_time)
212 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
213 restart_time = sxp.child_value(savedinfo, 'restart_time')
214 if restart_time is not None:
215 vm.restart_time = float(restart_time)
216 config = sxp.child_value(savedinfo, 'config')
217 if config:
218 d = vm.construct(config)
219 else:
220 vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
221 d = defer.succeed(vm)
222 vm.recreate = 0
223 vm.savedinfo = None
224 return d
226 def vm_restore(src, progress=0):
227 """Restore a VM from a disk image.
229 src saved state to restore
230 progress progress reporting flag
231 returns deferred
232 raises VmError for invalid configuration
233 """
234 vm = XendDomainInfo()
235 ostype = "linux" #todo Set from somewhere (store in the src?).
236 restorefn = getattr(xc, "%s_restore" % ostype)
237 d = restorefn(state_file=src, progress=progress)
238 dom = int(d['dom'])
239 if dom < 0:
240 raise VmError('restore failed')
241 try:
242 vmconfig = sxp.from_string(d['vmconfig'])
243 config = sxp.child_value(vmconfig, 'config')
244 except Exception, ex:
245 raise VmError('config error: ' + str(ex))
246 deferred = vm.dom_construct(dom, config)
247 def vifs_cb(val, vm):
248 vif_up(vm.ipaddrs)
249 return vm
250 deferred.addCallback(vifs_cb, vm)
251 return deferred
253 def dom_get(dom):
254 """Get info from xen for an existing domain.
256 @param dom: domain id
257 @return: info or None
258 """
259 domlist = xc.domain_getinfo(dom, 1)
260 if domlist and dom == domlist[0]['dom']:
261 return domlist[0]
262 return None
264 def append_deferred(dlist, v):
265 """Append a value to a deferred list if it is a deferred.
267 @param dlist: list of deferreds
268 @param v: value to add
269 """
270 if isinstance(v, defer.Deferred):
271 dlist.append(v)
273 def dlist_err(val):
274 """Error callback suitable for a deferred list.
275 In a deferred list the error callback is called with with Failure((error, index)).
276 This callback extracts the error and returns it.
278 @param val: Failure containing (error, index)
279 @type val: twisted.internet.failure.Failure
280 """
282 (error, index) = val.value
283 return error
285 class XendDomainInfo:
286 """Virtual machine object."""
288 """Minimum time between domain restarts in seconds.
289 """
290 MINIMUM_RESTART_TIME = 20
292 def __init__(self):
293 self.recreate = 0
294 self.restore = 0
295 self.config = None
296 self.id = None
297 self.dom = None
298 self.cpu_weight = 1
299 self.start_time = None
300 self.name = None
301 self.memory = None
302 self.image = None
303 self.ramdisk = None
304 self.cmdline = None
305 self.console = None
306 self.devices = {}
307 self.device_index = {}
308 self.configs = []
309 self.info = None
310 self.ipaddrs = []
311 self.blkif_backend = 0
312 self.netif_backend = 0
313 #todo: state: running, suspended
314 self.state = STATE_VM_OK
315 #todo: set to migrate info if migrating
316 self.migrate = None
317 self.restart_mode = RESTART_ONREBOOT
318 self.restart_state = None
319 self.restart_time = None
320 self.console_port = None
321 self.savedinfo = None
323 def setdom(self, dom):
324 """Set the domain id.
326 @param dom: domain id
327 """
328 self.dom = int(dom)
329 self.id = str(dom)
331 def update(self, info):
332 """Update with info from xc.domain_getinfo().
333 """
334 self.info = info
335 self.memory = self.info['mem_kb'] / 1024
337 def __str__(self):
338 s = "domain"
339 s += " id=" + self.id
340 s += " name=" + self.name
341 s += " memory=" + str(self.memory)
342 if self.console:
343 s += " console=" + str(self.console.console_port)
344 if self.image:
345 s += " image=" + self.image
346 s += ""
347 return s
349 __repr__ = __str__
351 def sxpr(self):
352 sxpr = ['domain',
353 ['id', self.id],
354 ['name', self.name],
355 ['memory', self.memory] ]
357 if self.info:
358 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
359 run = (self.info['running'] and 'r') or '-'
360 block = (self.info['blocked'] and 'b') or '-'
361 pause = (self.info['paused'] and 'p') or '-'
362 shut = (self.info['shutdown'] and 's') or '-'
363 crash = (self.info['crashed'] and 'c') or '-'
364 state = run + block + pause + shut + crash
365 sxpr.append(['state', state])
366 if self.info['shutdown']:
367 reason = shutdown_reason(self.info['shutdown_reason'])
368 sxpr.append(['shutdown_reason', reason])
369 sxpr.append(['cpu', self.info['cpu']])
370 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
372 if self.start_time:
373 up_time = time.time() - self.start_time
374 sxpr.append(['up_time', str(up_time) ])
375 sxpr.append(['start_time', str(self.start_time) ])
377 if self.console:
378 sxpr.append(self.console.sxpr())
379 if self.restart_state:
380 sxpr.append(['restart_state', self.restart_state])
381 if self.restart_time:
382 sxpr.append(['restart_time', str(self.restart_time)])
383 devs = self.sxpr_devices()
384 if devs:
385 sxpr.append(devs)
386 if self.config:
387 sxpr.append(['config', self.config])
388 return sxpr
390 def sxpr_devices(self):
391 sxpr = ['devices']
392 for devs in self.devices.values():
393 for dev in devs:
394 if hasattr(dev, 'sxpr'):
395 sxpr.append(dev.sxpr())
396 return sxpr
398 def check_name(self, name):
399 """Check if a vm name is valid. Valid names start with a non-digit
400 and contain alphabetic characters, digits, or characters in '_-.:/+'.
401 The same name cannot be used for more than one vm at the same time.
403 @param name: name
404 @raise: VMerror if invalid
405 """
406 if self.recreate: return
407 if name is None or name == '':
408 raise VmError('missing vm name')
409 if name[0] in string.digits:
410 raise VmError('invalid vm name')
411 for c in name:
412 if c in string.digits: continue
413 if c in '_-.:/+': continue
414 if c in string.ascii_letters: continue
415 raise VmError('invalid vm name')
416 dominfo = domain_exists(name)
417 # When creating or rebooting, a domain with my name should not exist.
418 # When restoring, a domain with my name will exist, but it should have
419 # my domain id.
420 if not dominfo:
421 return
422 if dominfo.is_terminated():
423 return
424 if not self.dom or (dominfo.dom != self.dom):
425 raise VmError('vm name clash: ' + name)
427 def construct(self, config):
428 """Construct the vm instance from its configuration.
430 @param config: configuration
431 @return: deferred
432 @raise: VmError on error
433 """
434 # todo - add support for scheduling params?
435 self.config = config
436 try:
437 self.name = sxp.child_value(config, 'name')
438 self.check_name(self.name)
439 try:
440 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
441 except:
442 raise VmError('invalid cpu weight')
443 self.memory = int(sxp.child_value(config, 'memory'))
444 if self.memory is None:
445 raise VmError('missing memory size')
446 cpu = sxp.child_value(config, 'cpu')
447 if self.recreate and self.dom and cpu is not None:
448 xc.domain_pincpu(self.dom, int(cpu))
450 self.init_domain()
451 self.configure_console()
452 self.construct_image()
453 self.configure_restart()
454 self.configure_backends()
455 deferred = self.configure()
456 def cberr(err):
457 self.destroy()
458 return err
459 deferred.addErrback(cberr)
460 except StandardError, ex:
461 # Catch errors, cleanup and re-raise.
462 self.destroy()
463 raise
464 return deferred
466 def construct_image(self):
467 """Construct the boot image for the domain.
469 @return vm
470 """
471 image = sxp.child_value(self.config, 'image')
472 if image is None:
473 raise VmError('missing image')
474 image_name = sxp.name(image)
475 if image_name is None:
476 raise VmError('missing image name')
477 image_handler = get_image_handler(image_name)
478 if image_handler is None:
479 raise VmError('unknown image type: ' + image_name)
480 image_handler(self, image)
481 return self
483 def config_devices(self, name):
484 """Get a list of the 'device' nodes of a given type from the config.
486 @param name: device type
487 @type name: string
488 @return: device configs
489 @rtype: list
490 """
491 devices = []
492 for d in sxp.children(self.config, 'device'):
493 dev = sxp.child0(d)
494 if dev is None: continue
495 if name == sxp.name(dev):
496 devices.append(dev)
497 return devices
499 def config_device(self, type, idx):
500 """Get a device config from the device nodes of a given type
501 from the config.
503 @param type: device type
504 @type type: string
505 @param idx: index
506 @type idx: int
507 @return config or None
508 """
509 devs = self.config_devices(type)
510 if 0 <= idx < len(devs):
511 return devs[idx]
512 else:
513 return None
515 def next_device_index(self, type):
516 """Get the next index for a given device type.
518 @param type: device type
519 @type type: string
520 @return device index
521 @rtype: int
522 """
523 idx = self.device_index.get(type, 0)
524 self.device_index[type] = idx + 1
525 return idx
527 def add_device(self, type, dev):
528 """Add a device to a virtual machine.
530 @param type: device type
531 @param dev: device to add
532 """
533 dl = self.devices.get(type, [])
534 dl.append(dev)
535 self.devices[type] = dl
537 def remove_device(self, type, dev):
538 """Remove a device from a virtual machine.
540 @param type: device type
541 @param dev: device
542 """
543 dl = self.devices.get(type, [])
544 if dev in dl:
545 dl.remove(dev)
547 def get_devices(self, type):
548 """Get a list of the devices of a given type.
550 @param type: device type
551 @return: devices
552 """
553 val = self.devices.get(type, [])
554 return val
556 def get_device_by_id(self, type, id):
557 """Get the device with the given id.
559 @param id: device id
560 @return: device or None
561 """
562 dl = self.get_devices(type)
563 for d in dl:
564 if d.getprop('id') == id:
565 return d
566 return None
568 def get_device_by_index(self, type, idx):
569 """Get the device with the given index.
571 @param idx: device index
572 @return: device or None
573 """
574 idx = str(idx)
575 dl = self.get_devices(type)
576 for d in dl:
577 if d.getidx() == idx:
578 return d
579 return None
581 def get_device_savedinfo(self, type, index):
582 val = None
583 if self.savedinfo is None:
584 return val
585 index = str(index)
586 devinfo = sxp.child(self.savedinfo, 'devices')
587 if devinfo is None:
588 return val
589 for d in sxp.children(devinfo, type):
590 dindex = sxp.child_value(d, 'index')
591 if dindex is None: continue
592 if str(dindex) == index:
593 val = d
594 break
595 return val
597 def get_device_recreate(self, type, index):
598 return self.get_device_savedinfo(type, index) or self.recreate
600 def add_config(self, val):
601 """Add configuration data to a virtual machine.
603 @param val: data to add
604 """
605 self.configs.append(val)
607 def destroy(self):
608 """Completely destroy the vm.
609 """
610 self.cleanup()
611 return self.destroy_domain()
613 def destroy_domain(self):
614 """Destroy the vm's domain.
615 The domain will not finally go away unless all vm
616 devices have been released.
617 """
618 if self.dom is None: return 0
619 self.destroy_console()
620 chan = xend.getDomChannel(self.dom)
621 if chan:
622 log.debug("Closing channel to domain %d", self.dom)
623 chan.close()
624 try:
625 return xc.domain_destroy(dom=self.dom)
626 except Exception, err:
627 log.exception("Domain destroy failed: %s", self.name)
629 def destroy_console(self):
630 if self.console:
631 if self.restart_pending():
632 self.console.deregisterChannel()
633 else:
634 log.debug('Closing console, domain %s', self.id)
635 self.console.close()
637 def cleanup(self):
638 """Cleanup vm resources: release devices.
639 """
640 self.state = STATE_VM_TERMINATED
641 self.release_devices()
643 def is_terminated(self):
644 """Check if a domain has been terminated.
645 """
646 return self.state == STATE_VM_TERMINATED
648 def release_devices(self):
649 """Release all vm devices.
650 """
651 self.release_vifs()
652 self.release_vbds()
654 self.devices = {}
655 self.device_index = {}
656 self.configs = []
657 self.ipaddrs = []
659 def release_vifs(self):
660 """Release vm virtual network devices (vifs).
661 """
662 if self.dom is None: return
663 ctrl = xend.netif_get(self.dom)
664 if ctrl:
665 log.debug("Destroying vifs for domain %d", self.dom)
666 ctrl.destroy()
668 def release_vbds(self):
669 """Release vm virtual block devices (vbds).
670 """
671 if self.dom is None: return
672 ctrl = xend.blkif_get(self.dom)
673 if ctrl:
674 log.debug("Destroying vbds for domain %d", self.dom)
675 ctrl.destroy()
677 def show(self):
678 """Print virtual machine info.
679 """
680 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
681 print "image:"
682 sxp.show(self.image)
683 print
684 for dl in self.devices:
685 for dev in dl:
686 print "device:"
687 sxp.show(dev)
688 print
689 for val in self.configs:
690 print "config:"
691 sxp.show(val)
692 print
693 print "]"
695 def init_domain(self):
696 """Initialize the domain memory.
697 """
698 if self.recreate:
699 return
700 if self.start_time is None:
701 self.start_time = time.time()
702 if self.restore:
703 return
704 dom = self.dom or 0
705 memory = self.memory
706 name = self.name
707 # If the name is over the xen limit, use the end of it.
708 if len(name) > MAX_DOMAIN_NAME:
709 name = name[-MAX_DOMAIN_NAME:]
710 try:
711 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
712 except:
713 raise VmError('invalid cpu')
714 cpu_weight = self.cpu_weight
715 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024,
716 cpu= cpu, cpu_weight= cpu_weight)
717 if dom <= 0:
718 raise VmError('Creating domain failed: name=%s memory=%d'
719 % (name, memory))
720 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
721 self.setdom(dom)
723 def build_domain(self, ostype, kernel, ramdisk, cmdline):
724 """Build the domain boot image.
725 """
726 if self.recreate or self.restore: return
727 if not os.path.isfile(kernel):
728 raise VmError('Kernel image does not exist: %s' % kernel)
729 if ramdisk and not os.path.isfile(ramdisk):
730 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
731 if len(cmdline) >= 256:
732 log.warning('kernel cmdline too long, domain %d', self.dom)
733 dom = self.dom
734 buildfn = getattr(xc, '%s_build' % ostype)
735 flags = 0
736 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
737 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
738 err = buildfn(dom = dom,
739 image = kernel,
740 control_evtchn = self.console.getRemotePort(),
741 cmdline = cmdline,
742 ramdisk = ramdisk,
743 flags = flags)
744 if err != 0:
745 raise VmError('Building domain failed: type=%s dom=%d err=%d'
746 % (ostype, dom, err))
748 def create_domain(self, ostype, kernel, ramdisk, cmdline):
749 """Create a domain. Builds the image but does not configure it.
751 @param ostype: OS type
752 @param kernel: kernel image
753 @param ramdisk: kernel ramdisk
754 @param cmdline: kernel commandline
755 """
757 self.create_channel()
758 if self.console:
759 self.console.registerChannel()
760 else:
761 self.console = xendConsole.console_create(
762 self.dom, console_port=self.console_port)
763 self.build_domain(ostype, kernel, ramdisk, cmdline)
764 self.image = kernel
765 self.ramdisk = ramdisk
766 self.cmdline = cmdline
768 def create_channel(self):
769 """Create the channel to the domain.
770 If saved info is available recreate the channel using the saved ports.
772 @return: channel
773 """
774 local = 0
775 remote = 1
776 if self.savedinfo:
777 consinfo = sxp.child(self.savedinfo, "console")
778 if consinfo:
779 local = int(sxp.child_value(consinfo, "local_port", 0))
780 remote = int(sxp.child_value(consinfo, "remote_port", 1))
781 return xend.createDomChannel(self.dom, local_port=local,
782 remote_port=remote)
784 def create_devices(self):
785 """Create the devices for a vm.
787 @return: Deferred
788 @raise: VmError for invalid devices
789 """
790 dlist = []
791 devices = sxp.children(self.config, 'device')
792 index = {}
793 for d in devices:
794 dev = sxp.child0(d)
795 if dev is None:
796 raise VmError('invalid device')
797 dev_name = sxp.name(dev)
798 dev_index = index.get(dev_name, 0)
799 dev_handler = get_device_handler(dev_name)
800 if dev_handler is None:
801 raise VmError('unknown device type: ' + dev_name)
802 v = dev_handler(self, dev, dev_index)
803 append_deferred(dlist, v)
804 index[dev_name] = dev_index + 1
805 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
806 deferred.addErrback(dlist_err)
807 return deferred
809 def device_create(self, dev_config):
810 """Create a new device.
812 @param dev_config: device configuration
813 @return: deferred
814 """
815 dev_name = sxp.name(dev_config)
816 dev_handler = get_device_handler(dev_name)
817 if dev_handler is None:
818 raise VmError('unknown device type: ' + dev_name)
819 devs = self.get_devices(dev_name)
820 dev_index = len(devs)
821 self.config.append(['device', dev_config])
822 d = dev_handler(self, dev_config, dev_index, change=1)
823 def cbok(dev):
824 return dev.sxpr()
825 d.addCallback(cbok)
826 return d
828 def device_configure(self, dev_config, idx):
829 """Configure an existing device.
831 @param dev_config: device configuration
832 @param idx: device index
833 """
834 type = sxp.name(dev_config)
835 dev = self.get_device_by_index(type, idx)
836 if not dev:
837 raise VmError('invalid device: %s %s' % (type, idx))
838 new_config = dev.configure(dev_config, change=1)
839 devs = self.devices.get(type)
840 index = devs.index(dev)
841 # Patch new config into device configs.
842 dev_configs = self.config_devices(type)
843 old_config = dev_configs[index]
844 dev_configs[index] = new_config
845 # Patch new config into vm config.
846 new_full_config = ['device', new_config]
847 old_full_config = ['device', old_config]
848 old_index = self.config.index(old_full_config)
849 self.config[old_index] = new_full_config
850 return new_config
852 def device_destroy(self, type, idx):
853 """Destroy a device.
855 @param type: device type
856 @param idx: device index
857 """
858 dev = self.get_device_by_index(type, idx)
859 if not dev:
860 raise VmError('invalid device: %s %s' % (type, idx))
861 devs = self.devices.get(type)
862 index = devs.index(dev)
863 dev_config = self.config_device(type, index)
864 if dev_config:
865 self.config.remove(['device', dev_config])
866 dev.destroy(change=1)
867 self.remove_device(type, dev)
869 def configure_memory(self):
870 """Configure vm memory limit.
871 """
872 maxmem = sxp.child_value(self.config, "maxmem")
873 if maxmem is None:
874 maxmem = self.memory
875 xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
877 def configure_console(self):
878 """Configure the vm console port.
879 """
880 x = sxp.child_value(self.config, 'console')
881 if x:
882 try:
883 port = int(x)
884 except:
885 raise VmError('invalid console:' + str(x))
886 self.console_port = port
888 def configure_restart(self):
889 """Configure the vm restart mode.
890 """
891 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
892 if r not in restart_modes:
893 raise VmError('invalid restart mode: ' + str(r))
894 self.restart_mode = r;
896 def restart_needed(self, reason):
897 """Determine if the vm needs to be restarted when shutdown
898 for the given reason.
900 @param reason: shutdown reason
901 @return 1 if needs restaert, 0 otherwise
902 """
903 if self.restart_mode == RESTART_NEVER:
904 return 0
905 if self.restart_mode == RESTART_ALWAYS:
906 return 1
907 if self.restart_mode == RESTART_ONREBOOT:
908 return reason == 'reboot'
909 return 0
911 def restart_cancel(self):
912 """Cancel a vm restart.
913 """
914 self.restart_state = None
916 def restarting(self):
917 """Put the vm into restart mode.
918 """
919 self.restart_state = STATE_RESTART_PENDING
921 def restart_pending(self):
922 """Test if the vm has a pending restart.
923 """
924 return self.restart_state == STATE_RESTART_PENDING
926 def restart_check(self):
927 """Check if domain restart is OK.
928 To prevent restart loops, raise an error if it is
929 less than MINIMUM_RESTART_TIME seconds since the last restart.
930 """
931 tnow = time.time()
932 if self.restart_time is not None:
933 tdelta = tnow - self.restart_time
934 if tdelta < self.MINIMUM_RESTART_TIME:
935 self.restart_cancel()
936 msg = 'VM %s restarting too fast' % self.name
937 log.error(msg)
938 raise VmError(msg)
939 self.restart_time = tnow
941 def restart(self):
942 """Restart the domain after it has exited.
943 Reuses the domain id and console port.
945 @return: deferred
946 """
947 try:
948 self.restart_check()
949 self.restart_state = STATE_RESTART_BOOTING
950 d = self.construct(self.config)
951 finally:
952 self.restart_state = None
953 return d
955 def configure_backends(self):
956 """Set configuration flags if the vm is a backend for netif or blkif.
957 Configure the backends to use for vbd and vif if specified.
958 """
959 for c in sxp.children(self.config, 'backend'):
960 v = sxp.child0(c)
961 name = sxp.name(v)
962 if name == 'blkif':
963 self.blkif_backend = 1
964 elif name == 'netif':
965 self.netif_backend = 1
966 else:
967 raise VmError('invalid backend type:' + str(name))
969 def configure(self):
970 """Configure a vm.
972 @return: deferred - calls callback with vm
973 """
974 d = self.create_devices()
975 d.addCallback(lambda x: self.create_blkif())
976 d.addCallback(self._configure)
977 return d
979 def _configure(self, val):
980 d = self.configure_fields()
981 def cbok(results):
982 return self
983 def cberr(err):
984 self.destroy()
985 return err
986 d.addCallback(cbok)
987 d.addErrback(cberr)
988 return d
990 def create_blkif(self):
991 """Create the block device interface (blkif) for the vm.
992 The vm needs a blkif even if it doesn't have any disks
993 at creation time, for example when it uses NFS root.
995 @return: deferred
996 """
997 if self.get_devices("vbd") == []:
998 ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
999 back = ctrl.getBackendInterface(0)
1000 return back.connect(recreate=self.recreate)
1001 else:
1002 return None
1004 def dom_construct(self, dom, config):
1005 """Construct a vm for an existing domain.
1007 @param dom: domain id
1008 @param config: domain configuration
1009 @return: deferred
1010 """
1011 d = dom_get(dom)
1012 if not d:
1013 raise VmError("Domain not found: %d" % dom)
1014 try:
1015 self.restore = 1
1016 self.setdom(dom)
1017 #self.name = d['name']
1018 self.memory = d['mem_kb']/1024
1019 deferred = self.construct(config)
1020 finally:
1021 self.restore = 0
1022 return deferred
1024 def configure_fields(self):
1025 """Process the vm configuration fields using the registered handlers.
1026 """
1027 dlist = []
1028 index = {}
1029 for field in sxp.children(self.config):
1030 field_name = sxp.name(field)
1031 field_index = index.get(field_name, 0)
1032 field_handler = get_config_handler(field_name)
1033 # Ignore unknown fields. Warn?
1034 if field_handler:
1035 v = field_handler(self, self.config, field, field_index)
1036 append_deferred(dlist, v)
1037 else:
1038 log.warning("Unknown config field %s", field_name)
1039 index[field_name] = field_index + 1
1040 d = defer.DeferredList(dlist, fireOnOneErrback=1)
1041 d.addErrback(dlist_err)
1042 return d
1045 def vm_image_linux(vm, image):
1046 """Create a VM for a linux image.
1048 @param name: vm name
1049 @param memory: vm memory
1050 @param image: image config
1051 @return: vm
1052 """
1053 kernel = sxp.child_value(image, "kernel")
1054 cmdline = ""
1055 ip = sxp.child_value(image, "ip", None)
1056 if ip:
1057 cmdline += " ip=" + ip
1058 root = sxp.child_value(image, "root")
1059 if root:
1060 cmdline += " root=" + root
1061 args = sxp.child_value(image, "args")
1062 if args:
1063 cmdline += " " + args
1064 ramdisk = sxp.child_value(image, "ramdisk", '')
1065 vm.create_domain("linux", kernel, ramdisk, cmdline)
1066 return vm
1068 def vm_dev_vif(vm, val, index, change=0):
1069 """Create a virtual network interface (vif).
1071 @param vm: virtual machine
1072 @param val: vif config
1073 @param index: vif index
1074 @return: deferred
1075 """
1076 vif = vm.next_device_index('vif')
1077 vmac = sxp.child_value(val, "mac")
1078 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1079 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1080 recreate = vm.get_device_recreate('vif', index)
1081 defer = ctrl.attachDevice(vif, val, recreate=recreate)
1082 def cbok(dev):
1083 dev.vifctl('up', vmname=vm.name)
1084 dev.setIndex(index)
1085 vm.add_device('vif', dev)
1086 if change:
1087 dev.interfaceChanged()
1088 return dev
1089 defer.addCallback(cbok)
1090 return defer
1092 def vm_dev_vbd(vm, val, index, change=0):
1093 """Create a virtual block device (vbd).
1095 @param vm: virtual machine
1096 @param val: vbd config
1097 @param index: vbd index
1098 @return: deferred
1099 """
1100 idx = vm.next_device_index('vbd')
1101 uname = sxp.child_value(val, 'uname')
1102 log.debug("Creating vbd dom=%d uname=%s", vm.dom, uname)
1103 ctrl = xend.blkif_create(vm.dom, recreate=vm.recreate)
1104 recreate = vm.get_device_recreate('vbd', index)
1105 defer = ctrl.attachDevice(idx, val, recreate=recreate)
1106 def cbok(dev):
1107 dev.setIndex(index)
1108 vm.add_device('vbd', dev)
1109 if change:
1110 dev.interfaceChanged()
1111 return dev
1112 defer.addCallback(cbok)
1113 return defer
1115 def parse_pci(val):
1116 """Parse a pci field.
1117 """
1118 if isinstance(val, types.StringType):
1119 radix = 10
1120 if val.startswith('0x') or val.startswith('0X'):
1121 radix = 16
1122 v = int(val, radix)
1123 else:
1124 v = val
1125 return v
1127 def vm_dev_pci(vm, val, index, change=0):
1128 """Add a pci device.
1130 @param vm: virtual machine
1131 @param val: device configuration
1132 @param index: device index
1133 @return: 0 on success
1134 """
1135 bus = sxp.child_value(val, 'bus')
1136 if not bus:
1137 raise VmError('pci: Missing bus')
1138 dev = sxp.child_value(val, 'dev')
1139 if not dev:
1140 raise VmError('pci: Missing dev')
1141 func = sxp.child_value(val, 'func')
1142 if not func:
1143 raise VmError('pci: Missing func')
1144 try:
1145 bus = parse_pci(bus)
1146 dev = parse_pci(dev)
1147 func = parse_pci(func)
1148 except:
1149 raise VmError('pci: invalid parameter')
1150 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1151 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1152 func=func, enable=1)
1153 if rc < 0:
1154 #todo non-fatal
1155 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1156 (bus, dev, func))
1157 return rc
1160 def vm_field_ignore(vm, config, val, index):
1161 """Dummy config field handler used for fields with built-in handling.
1163 @param vm: virtual machine
1164 @param config: vm config
1165 @param val: config field
1166 @param index: field index
1167 """
1168 pass
1170 def vm_field_maxmem(vm, config, val, index):
1171 """Configure vm memory limit.
1173 @param vm: virtual machine
1174 @param config: vm config
1175 @param val: config field
1176 @param index: field index
1177 """
1178 maxmem = sxp.child0(val)
1179 if maxmem is None:
1180 maxmem = vm.memory
1181 try:
1182 maxmem = int(maxmem)
1183 except:
1184 raise VmError("invalid maxmem: " + str(maxmem))
1185 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1187 # Register image handlers.
1188 add_image_handler('linux', vm_image_linux)
1190 # Register device handlers.
1191 add_device_handler('vif', vm_dev_vif)
1192 add_device_handler('vbd', vm_dev_vbd)
1193 add_device_handler('pci', vm_dev_pci)
1195 # Ignore the fields we already handle.
1196 add_config_handler('name', vm_field_ignore)
1197 add_config_handler('memory', vm_field_ignore)
1198 add_config_handler('cpu', vm_field_ignore)
1199 add_config_handler('cpu_weight', vm_field_ignore)
1200 add_config_handler('console', vm_field_ignore)
1201 add_config_handler('image', vm_field_ignore)
1202 add_config_handler('device', vm_field_ignore)
1203 add_config_handler('backend', vm_field_ignore)
1205 # Register other config handlers.
1206 add_config_handler('maxmem', vm_field_maxmem)