ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 3559:80651f13b9c0

bitkeeper revision 1.1159.233.1 (41f910e9AmctVZRfu78RAYbPnw3W8g)

Merge scramble.cl.cam.ac.uk:/local/scratch/kaf24/xen-2.0-testing.bk
into scramble.cl.cam.ac.uk:/local/scratch/kaf24/xen-unstable.bk
author kaf24@scramble.cl.cam.ac.uk
date Thu Jan 27 16:03:53 2005 +0000 (2005-01-27)
parents 3ced9b0f4dab 77732aef762e
children 30ee9c427a5b bc0fbb38cb25 20c37da2d56b 20c37da2d56b
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
23 from xen.xend.server import channel
25 import sxp
27 import XendConsole
28 xendConsole = XendConsole.instance()
29 from XendLogging import log
30 from XendRoot import get_component
32 import server.SrvDaemon
33 xend = server.SrvDaemon.instance()
35 from XendError import VmError
37 """The length of domain names that Xen can handle.
38 The names stored in Xen itself are not used for much, and
39 xend can handle domain names of any length.
40 """
41 MAX_DOMAIN_NAME = 15
43 """Flag for a block device backend domain."""
44 SIF_BLK_BE_DOMAIN = (1<<4)
46 """Flag for a net device backend domain."""
47 SIF_NET_BE_DOMAIN = (1<<5)
49 """Shutdown code for poweroff."""
50 DOMAIN_POWEROFF = 0
51 """Shutdown code for reboot."""
52 DOMAIN_REBOOT = 1
53 """Shutdown code for suspend."""
54 DOMAIN_SUSPEND = 2
56 """Map shutdown codes to strings."""
57 shutdown_reasons = {
58 DOMAIN_POWEROFF: "poweroff",
59 DOMAIN_REBOOT : "reboot",
60 DOMAIN_SUSPEND : "suspend" }
62 RESTART_ALWAYS = 'always'
63 RESTART_ONREBOOT = 'onreboot'
64 RESTART_NEVER = 'never'
66 restart_modes = [
67 RESTART_ALWAYS,
68 RESTART_ONREBOOT,
69 RESTART_NEVER,
70 ]
72 STATE_RESTART_PENDING = 'pending'
73 STATE_RESTART_BOOTING = 'booting'
75 STATE_VM_OK = "ok"
76 STATE_VM_TERMINATED = "terminated"
79 def domain_exists(name):
80 # See comment in XendDomain constructor.
81 xd = get_component('xen.xend.XendDomain')
82 return xd.domain_exists(name)
84 def shutdown_reason(code):
85 """Get a shutdown reason from a code.
87 @param code: shutdown code
88 @type code: int
89 @return: shutdown reason
90 @rtype: string
91 """
92 return shutdown_reasons.get(code, "?")
94 def vif_up(iplist):
95 """send an unsolicited ARP reply for all non link-local IP addresses.
97 @param iplist: IP addresses
98 """
100 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
102 def get_ip_nonlocal_bind():
103 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
105 def set_ip_nonlocal_bind(v):
106 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
108 def link_local(ip):
109 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
111 def arping(ip, gw):
112 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
113 log.debug(cmd)
114 os.system(cmd)
116 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
117 nlb = get_ip_nonlocal_bind()
118 if not nlb: set_ip_nonlocal_bind(1)
119 try:
120 for ip in iplist:
121 if not link_local(ip):
122 arping(ip, gateway)
123 finally:
124 if not nlb: set_ip_nonlocal_bind(0)
126 config_handlers = {}
128 def add_config_handler(name, h):
129 """Add a handler for a config field.
131 @param name: field name
132 @param h: handler: fn(vm, config, field, index)
133 """
134 config_handlers[name] = h
136 def get_config_handler(name):
137 """Get a handler for a config field.
139 returns handler or None
140 """
141 return config_handlers.get(name)
143 """Table of handlers for virtual machine images.
144 Indexed by image type.
145 """
146 image_handlers = {}
148 def add_image_handler(name, h):
149 """Add a handler for an image type
150 @param name: image type
151 @param h: handler: fn(config, name, memory, image)
152 """
153 image_handlers[name] = h
155 def get_image_handler(name):
156 """Get the handler for an image type.
157 @param name: image type
158 @return: handler or None
159 """
160 return image_handlers.get(name)
162 """Table of handlers for devices.
163 Indexed by device type.
164 """
165 device_handlers = {}
167 def add_device_handler(name, h):
168 """Add a handler for a device type.
170 @param name: device type
171 @param h: handler: fn(vm, dev)
172 """
173 device_handlers[name] = h
175 def get_device_handler(name):
176 """Get the handler for a device type.
178 @param name : device type
179 @return; handler or None
180 """
181 return device_handlers.get(name)
183 def vm_create(config):
184 """Create a VM from a configuration.
185 If a vm has been partially created and there is an error it
186 is destroyed.
188 @param config configuration
189 @return: Deferred
190 @raise: VmError for invalid configuration
191 """
192 vm = XendDomainInfo()
193 return vm.construct(config)
195 def vm_recreate(savedinfo, info):
196 """Create the VM object for an existing domain.
198 @param savedinfo: saved info from the domain DB
199 @type savedinfo: sxpr
200 @param info: domain info from xc
201 @type info: xc domain dict
202 @return: deferred
203 """
204 vm = XendDomainInfo()
205 vm.recreate = 1
206 vm.savedinfo = savedinfo
207 vm.setdom(info['dom'])
208 #vm.name = info['name']
209 vm.memory = info['mem_kb']/1024
210 start_time = sxp.child_value(savedinfo, 'start_time')
211 if start_time is not None:
212 vm.start_time = float(start_time)
213 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
214 restart_time = sxp.child_value(savedinfo, 'restart_time')
215 if restart_time is not None:
216 vm.restart_time = float(restart_time)
217 config = sxp.child_value(savedinfo, 'config')
218 if config:
219 d = vm.construct(config)
220 else:
221 vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
222 d = defer.succeed(vm)
223 vm.recreate = 0
224 vm.savedinfo = None
225 return d
227 def vm_restore(src, progress=0):
228 """Restore a VM from a disk image.
230 src saved state to restore
231 progress progress reporting flag
232 returns deferred
233 raises VmError for invalid configuration
234 """
235 vm = XendDomainInfo()
236 ostype = "linux" #todo Set from somewhere (store in the src?).
237 restorefn = getattr(xc, "%s_restore" % ostype)
238 d = restorefn(state_file=src, progress=progress)
239 dom = int(d['dom'])
240 if dom < 0:
241 raise VmError('restore failed')
242 try:
243 vmconfig = sxp.from_string(d['vmconfig'])
244 config = sxp.child_value(vmconfig, 'config')
245 except Exception, ex:
246 raise VmError('config error: ' + str(ex))
247 deferred = vm.dom_construct(dom, config)
248 def vifs_cb(val, vm):
249 vif_up(vm.ipaddrs)
250 return vm
251 deferred.addCallback(vifs_cb, vm)
252 return deferred
254 def dom_get(dom):
255 """Get info from xen for an existing domain.
257 @param dom: domain id
258 @return: info or None
259 """
260 domlist = xc.domain_getinfo(dom, 1)
261 if domlist and dom == domlist[0]['dom']:
262 return domlist[0]
263 return None
265 def append_deferred(dlist, v):
266 """Append a value to a deferred list if it is a deferred.
268 @param dlist: list of deferreds
269 @param v: value to add
270 """
271 if isinstance(v, defer.Deferred):
272 dlist.append(v)
274 def dlist_err(val):
275 """Error callback suitable for a deferred list.
276 In a deferred list the error callback is called with with Failure((error, index)).
277 This callback extracts the error and returns it.
279 @param val: Failure containing (error, index)
280 @type val: twisted.internet.failure.Failure
281 """
283 (error, index) = val.value
284 return error
286 class XendDomainInfo:
287 """Virtual machine object."""
289 """Minimum time between domain restarts in seconds.
290 """
291 MINIMUM_RESTART_TIME = 20
293 def __init__(self):
294 self.recreate = 0
295 self.restore = 0
296 self.config = None
297 self.id = None
298 self.dom = None
299 self.cpu_weight = 1
300 self.start_time = None
301 self.name = None
302 self.memory = None
303 self.image = None
304 self.ramdisk = None
305 self.cmdline = None
306 self.console = None
307 self.devices = {}
308 self.device_index = {}
309 self.configs = []
310 self.info = None
311 self.ipaddrs = []
312 self.blkif_backend = 0
313 self.netif_backend = 0
314 #todo: state: running, suspended
315 self.state = STATE_VM_OK
316 #todo: set to migrate info if migrating
317 self.migrate = None
318 self.restart_mode = RESTART_ONREBOOT
319 self.restart_state = None
320 self.restart_time = None
321 self.console_port = None
322 self.savedinfo = None
323 self.is_vmx = 0
324 self.vcpus = 1
326 def setdom(self, dom):
327 """Set the domain id.
329 @param dom: domain id
330 """
331 self.dom = int(dom)
332 self.id = str(dom)
334 def update(self, info):
335 """Update with info from xc.domain_getinfo().
336 """
337 self.info = info
338 self.memory = self.info['mem_kb'] / 1024
340 def __str__(self):
341 s = "domain"
342 s += " id=" + self.id
343 s += " name=" + self.name
344 s += " memory=" + str(self.memory)
345 if self.console:
346 s += " console=" + str(self.console.console_port)
347 if self.image:
348 s += " image=" + self.image
349 s += ""
350 return s
352 __repr__ = __str__
354 def sxpr(self):
355 sxpr = ['domain',
356 ['id', self.id],
357 ['name', self.name],
358 ['memory', self.memory] ]
360 if self.info:
361 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
362 run = (self.info['running'] and 'r') or '-'
363 block = (self.info['blocked'] and 'b') or '-'
364 pause = (self.info['paused'] and 'p') or '-'
365 shut = (self.info['shutdown'] and 's') or '-'
366 crash = (self.info['crashed'] and 'c') or '-'
367 state = run + block + pause + shut + crash
368 sxpr.append(['state', state])
369 if self.info['shutdown']:
370 reason = shutdown_reason(self.info['shutdown_reason'])
371 sxpr.append(['shutdown_reason', reason])
372 sxpr.append(['cpu', self.info['cpu']])
373 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
375 if self.start_time:
376 up_time = time.time() - self.start_time
377 sxpr.append(['up_time', str(up_time) ])
378 sxpr.append(['start_time', str(self.start_time) ])
380 if self.console:
381 sxpr.append(self.console.sxpr())
382 if self.restart_state:
383 sxpr.append(['restart_state', self.restart_state])
384 if self.restart_time:
385 sxpr.append(['restart_time', str(self.restart_time)])
386 devs = self.sxpr_devices()
387 if devs:
388 sxpr.append(devs)
389 if self.config:
390 sxpr.append(['config', self.config])
391 return sxpr
393 def sxpr_devices(self):
394 sxpr = ['devices']
395 for devs in self.devices.values():
396 for dev in devs:
397 if hasattr(dev, 'sxpr'):
398 sxpr.append(dev.sxpr())
399 return sxpr
401 def check_name(self, name):
402 """Check if a vm name is valid. Valid names start with a non-digit
403 and contain alphabetic characters, digits, or characters in '_-.:/+'.
404 The same name cannot be used for more than one vm at the same time.
406 @param name: name
407 @raise: VMerror if invalid
408 """
409 if self.recreate: return
410 if name is None or name == '':
411 raise VmError('missing vm name')
412 if name[0] in string.digits:
413 raise VmError('invalid vm name')
414 for c in name:
415 if c in string.digits: continue
416 if c in '_-.:/+': continue
417 if c in string.ascii_letters: continue
418 raise VmError('invalid vm name')
419 dominfo = domain_exists(name)
420 # When creating or rebooting, a domain with my name should not exist.
421 # When restoring, a domain with my name will exist, but it should have
422 # my domain id.
423 if not dominfo:
424 return
425 if dominfo.is_terminated():
426 return
427 if not self.dom or (dominfo.dom != self.dom):
428 raise VmError('vm name clash: ' + name)
430 def construct(self, config):
431 """Construct the vm instance from its configuration.
433 @param config: configuration
434 @return: deferred
435 @raise: VmError on error
436 """
437 # todo - add support for scheduling params?
438 self.config = config
439 try:
440 self.name = sxp.child_value(config, 'name')
441 self.check_name(self.name)
442 try:
443 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
444 except:
445 raise VmError('invalid cpu weight')
446 self.memory = int(sxp.child_value(config, 'memory'))
447 if self.memory is None:
448 raise VmError('missing memory size')
449 cpu = sxp.child_value(config, 'cpu')
450 if self.recreate and self.dom and cpu is not None:
451 xc.domain_pincpu(self.dom, int(cpu))
452 try:
453 image = sxp.child_value(self.config, 'image')
454 self.vcpus = int(sxp.child_value(image, 'vcpus'))
455 except:
456 raise VmError('invalid vcpus value')
458 self.init_domain()
459 self.configure_console()
460 self.configure_backends()
461 self.construct_image()
462 self.configure_restart()
463 deferred = self.configure()
464 def cberr(err):
465 self.destroy()
466 return err
467 deferred.addErrback(cberr)
468 except StandardError, ex:
469 # Catch errors, cleanup and re-raise.
470 self.destroy()
471 raise
472 return deferred
474 def construct_image(self):
475 """Construct the boot image for the domain.
477 @return vm
478 """
479 image = sxp.child_value(self.config, 'image')
480 if image is None:
481 raise VmError('missing image')
482 image_name = sxp.name(image)
483 if image_name is None:
484 raise VmError('missing image name')
485 image_handler = get_image_handler(image_name)
486 if image_handler is None:
487 raise VmError('unknown image type: ' + image_name)
488 image_handler(self, image)
489 return self
491 def config_devices(self, name):
492 """Get a list of the 'device' nodes of a given type from the config.
494 @param name: device type
495 @type name: string
496 @return: device configs
497 @rtype: list
498 """
499 devices = []
500 for d in sxp.children(self.config, 'device'):
501 dev = sxp.child0(d)
502 if dev is None: continue
503 if name == sxp.name(dev):
504 devices.append(dev)
505 return devices
507 def config_device(self, type, idx):
508 """Get a device config from the device nodes of a given type
509 from the config.
511 @param type: device type
512 @type type: string
513 @param idx: index
514 @type idx: int
515 @return config or None
516 """
517 devs = self.config_devices(type)
518 if 0 <= idx < len(devs):
519 return devs[idx]
520 else:
521 return None
523 def next_device_index(self, type):
524 """Get the next index for a given device type.
526 @param type: device type
527 @type type: string
528 @return device index
529 @rtype: int
530 """
531 idx = self.device_index.get(type, 0)
532 self.device_index[type] = idx + 1
533 return idx
535 def add_device(self, type, dev):
536 """Add a device to a virtual machine.
538 @param type: device type
539 @param dev: device to add
540 """
541 dl = self.devices.get(type, [])
542 dl.append(dev)
543 self.devices[type] = dl
545 def remove_device(self, type, dev):
546 """Remove a device from a virtual machine.
548 @param type: device type
549 @param dev: device
550 """
551 dl = self.devices.get(type, [])
552 if dev in dl:
553 dl.remove(dev)
555 def get_devices(self, type):
556 """Get a list of the devices of a given type.
558 @param type: device type
559 @return: devices
560 """
561 val = self.devices.get(type, [])
562 return val
564 def get_device_by_id(self, type, id):
565 """Get the device with the given id.
567 @param id: device id
568 @return: device or None
569 """
570 dl = self.get_devices(type)
571 for d in dl:
572 if d.getprop('id') == id:
573 return d
574 return None
576 def get_device_by_index(self, type, idx):
577 """Get the device with the given index.
579 @param idx: device index
580 @return: device or None
581 """
582 idx = str(idx)
583 dl = self.get_devices(type)
584 for d in dl:
585 if d.getidx() == idx:
586 return d
587 return None
589 def get_device_savedinfo(self, type, index):
590 val = None
591 if self.savedinfo is None:
592 return val
593 index = str(index)
594 devinfo = sxp.child(self.savedinfo, 'devices')
595 if devinfo is None:
596 return val
597 for d in sxp.children(devinfo, type):
598 dindex = sxp.child_value(d, 'index')
599 if dindex is None: continue
600 if str(dindex) == index:
601 val = d
602 break
603 return val
605 def get_device_recreate(self, type, index):
606 return self.get_device_savedinfo(type, index) or self.recreate
608 def add_config(self, val):
609 """Add configuration data to a virtual machine.
611 @param val: data to add
612 """
613 self.configs.append(val)
615 def destroy(self):
616 """Completely destroy the vm.
617 """
618 self.cleanup()
619 return self.destroy_domain()
621 def destroy_domain(self):
622 """Destroy the vm's domain.
623 The domain will not finally go away unless all vm
624 devices have been released.
625 """
626 if self.dom is None: return 0
627 self.destroy_console()
628 chan = xend.getDomChannel(self.dom)
629 if chan:
630 log.debug("Closing channel to domain %d", self.dom)
631 chan.close()
632 try:
633 return xc.domain_destroy(dom=self.dom)
634 except Exception, err:
635 log.exception("Domain destroy failed: %s", self.name)
637 def destroy_console(self):
638 if self.console:
639 if self.restart_pending():
640 self.console.deregisterChannel()
641 else:
642 log.debug('Closing console, domain %s', self.id)
643 self.console.close()
645 def cleanup(self):
646 """Cleanup vm resources: release devices.
647 """
648 self.state = STATE_VM_TERMINATED
649 self.release_devices()
651 def is_terminated(self):
652 """Check if a domain has been terminated.
653 """
654 return self.state == STATE_VM_TERMINATED
656 def release_devices(self):
657 """Release all vm devices.
658 """
659 self.release_vifs()
660 self.release_vbds()
661 self.release_usbifs()
663 self.devices = {}
664 self.device_index = {}
665 self.configs = []
666 self.ipaddrs = []
668 def release_vifs(self):
669 """Release vm virtual network devices (vifs).
670 """
671 if self.dom is None: return
672 ctrl = xend.netif_get(self.dom)
673 if ctrl:
674 log.debug("Destroying vifs for domain %d", self.dom)
675 ctrl.destroy()
677 def release_vbds(self):
678 """Release vm virtual block devices (vbds).
679 """
680 if self.dom is None: return
681 ctrl = xend.blkif_get(self.dom)
682 if ctrl:
683 log.debug("Destroying vbds for domain %d", self.dom)
684 ctrl.destroy()
686 def release_usbifs(self):
687 """Release vm virtual USB devices (usbifs).
688 """
689 if self.dom is None: return
690 ctrl = xend.usbif_get(self.dom)
691 if ctrl:
692 log.debug("Destroying usbifs for domain %d", self.dom)
693 ctrl.destroy()
695 def show(self):
696 """Print virtual machine info.
697 """
698 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
699 print "image:"
700 sxp.show(self.image)
701 print
702 for dl in self.devices:
703 for dev in dl:
704 print "device:"
705 sxp.show(dev)
706 print
707 for val in self.configs:
708 print "config:"
709 sxp.show(val)
710 print
711 print "]"
713 def init_domain(self):
714 """Initialize the domain memory.
715 """
716 if self.recreate:
717 return
718 if self.start_time is None:
719 self.start_time = time.time()
720 if self.restore:
721 return
722 dom = self.dom or 0
723 memory = self.memory
724 name = self.name
725 # If the name is over the xen limit, use the end of it.
726 if len(name) > MAX_DOMAIN_NAME:
727 name = name[-MAX_DOMAIN_NAME:]
728 try:
729 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
730 except:
731 raise VmError('invalid cpu')
732 cpu_weight = self.cpu_weight
733 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024,
734 cpu= cpu, cpu_weight= cpu_weight)
735 if dom <= 0:
736 raise VmError('Creating domain failed: name=%s memory=%d'
737 % (name, memory))
738 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
739 self.setdom(dom)
741 def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
742 """Build the domain boot image.
743 """
744 if self.recreate or self.restore: return
745 if not os.path.isfile(kernel):
746 raise VmError('Kernel image does not exist: %s' % kernel)
747 if ramdisk and not os.path.isfile(ramdisk):
748 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
749 if len(cmdline) >= 256:
750 log.warning('kernel cmdline too long, domain %d', self.dom)
751 dom = self.dom
752 buildfn = getattr(xc, '%s_build' % ostype)
753 flags = 0
754 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
755 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
756 if ostype == "vmx":
757 err = buildfn(dom = dom,
758 image = kernel,
759 control_evtchn = 0,
760 memmap = memmap,
761 cmdline = cmdline,
762 ramdisk = ramdisk,
763 flags = flags)
764 else:
765 log.warning('building dom with %d vcpus', self.vcpus)
766 err = buildfn(dom = dom,
767 image = kernel,
768 control_evtchn = self.console.getRemotePort(),
769 cmdline = cmdline,
770 ramdisk = ramdisk,
771 flags = flags,
772 vcpus = self.vcpus)
773 if err != 0:
774 raise VmError('Building domain failed: type=%s dom=%d err=%d'
775 % (ostype, dom, err))
777 def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
778 """Create a domain. Builds the image but does not configure it.
780 @param ostype: OS type
781 @param kernel: kernel image
782 @param ramdisk: kernel ramdisk
783 @param cmdline: kernel commandline
784 """
786 self.create_channel()
787 if self.console:
788 self.console.registerChannel()
789 else:
790 self.console = xendConsole.console_create(
791 self.dom, console_port=self.console_port)
792 self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
793 self.image = kernel
794 self.ramdisk = ramdisk
795 self.cmdline = cmdline
797 def create_channel(self):
798 """Create the channel to the domain.
799 If saved info is available recreate the channel using the saved ports.
801 @return: channel
802 """
803 local = 0
804 remote = 1
805 if self.savedinfo:
806 consinfo = sxp.child(self.savedinfo, "console")
807 if consinfo:
808 local = int(sxp.child_value(consinfo, "local_port", 0))
809 remote = int(sxp.child_value(consinfo, "remote_port", 1))
810 return xend.createDomChannel(self.dom, local_port=local,
811 remote_port=remote)
813 def create_devices(self):
814 """Create the devices for a vm.
816 @return: Deferred
817 @raise: VmError for invalid devices
818 """
819 dlist = []
820 devices = sxp.children(self.config, 'device')
821 index = {}
822 for d in devices:
823 dev = sxp.child0(d)
824 if dev is None:
825 raise VmError('invalid device')
826 dev_name = sxp.name(dev)
827 dev_index = index.get(dev_name, 0)
828 dev_handler = get_device_handler(dev_name)
829 if dev_handler is None:
830 raise VmError('unknown device type: ' + dev_name)
831 v = dev_handler(self, dev, dev_index)
832 append_deferred(dlist, v)
833 index[dev_name] = dev_index + 1
834 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
835 deferred.addErrback(dlist_err)
836 if self.is_vmx:
837 device_model = sxp.child_value(self.config, 'device_model')
838 device_config = sxp.child_value(self.config, 'device_config')
839 memory = sxp.child_value(self.config, "memory")
840 # Create an event channel
841 device_channel = channel.eventChannel(0, self.dom)
842 # Fork and exec device_model -f device_config <port>
843 os.system(device_model
844 + " -f %s" % device_config
845 + " -d %d" % self.dom
846 + " -p %d" % device_channel['port1']
847 + " -m %s &" % memory)
848 return deferred
850 def device_create(self, dev_config):
851 """Create a new device.
853 @param dev_config: device configuration
854 @return: deferred
855 """
856 dev_name = sxp.name(dev_config)
857 dev_handler = get_device_handler(dev_name)
858 if dev_handler is None:
859 raise VmError('unknown device type: ' + dev_name)
860 devs = self.get_devices(dev_name)
861 dev_index = len(devs)
862 self.config.append(['device', dev_config])
863 d = dev_handler(self, dev_config, dev_index, change=1)
864 def cbok(dev):
865 return dev.sxpr()
866 d.addCallback(cbok)
867 return d
869 def device_configure(self, dev_config, idx):
870 """Configure an existing device.
872 @param dev_config: device configuration
873 @param idx: device index
874 """
875 type = sxp.name(dev_config)
876 dev = self.get_device_by_index(type, idx)
877 if not dev:
878 raise VmError('invalid device: %s %s' % (type, idx))
879 new_config = dev.configure(dev_config, change=1)
880 devs = self.devices.get(type)
881 index = devs.index(dev)
882 # Patch new config into device configs.
883 dev_configs = self.config_devices(type)
884 old_config = dev_configs[index]
885 dev_configs[index] = new_config
886 # Patch new config into vm config.
887 new_full_config = ['device', new_config]
888 old_full_config = ['device', old_config]
889 old_index = self.config.index(old_full_config)
890 self.config[old_index] = new_full_config
891 return new_config
893 def device_destroy(self, type, idx):
894 """Destroy a device.
896 @param type: device type
897 @param idx: device index
898 """
899 dev = self.get_device_by_index(type, idx)
900 if not dev:
901 raise VmError('invalid device: %s %s' % (type, idx))
902 devs = self.devices.get(type)
903 index = devs.index(dev)
904 dev_config = self.config_device(type, index)
905 if dev_config:
906 self.config.remove(['device', dev_config])
907 dev.destroy(change=1)
908 self.remove_device(type, dev)
910 def configure_memory(self):
911 """Configure vm memory limit.
912 """
913 maxmem = sxp.child_value(self.config, "maxmem")
914 if maxmem is None:
915 maxmem = self.memory
916 xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
918 def configure_console(self):
919 """Configure the vm console port.
920 """
921 x = sxp.child_value(self.config, 'console')
922 if x:
923 try:
924 port = int(x)
925 except:
926 raise VmError('invalid console:' + str(x))
927 self.console_port = port
929 def configure_restart(self):
930 """Configure the vm restart mode.
931 """
932 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
933 if r not in restart_modes:
934 raise VmError('invalid restart mode: ' + str(r))
935 self.restart_mode = r;
937 def restart_needed(self, reason):
938 """Determine if the vm needs to be restarted when shutdown
939 for the given reason.
941 @param reason: shutdown reason
942 @return 1 if needs restaert, 0 otherwise
943 """
944 if self.restart_mode == RESTART_NEVER:
945 return 0
946 if self.restart_mode == RESTART_ALWAYS:
947 return 1
948 if self.restart_mode == RESTART_ONREBOOT:
949 return reason == 'reboot'
950 return 0
952 def restart_cancel(self):
953 """Cancel a vm restart.
954 """
955 self.restart_state = None
957 def restarting(self):
958 """Put the vm into restart mode.
959 """
960 self.restart_state = STATE_RESTART_PENDING
962 def restart_pending(self):
963 """Test if the vm has a pending restart.
964 """
965 return self.restart_state == STATE_RESTART_PENDING
967 def restart_check(self):
968 """Check if domain restart is OK.
969 To prevent restart loops, raise an error if it is
970 less than MINIMUM_RESTART_TIME seconds since the last restart.
971 """
972 tnow = time.time()
973 if self.restart_time is not None:
974 tdelta = tnow - self.restart_time
975 if tdelta < self.MINIMUM_RESTART_TIME:
976 self.restart_cancel()
977 msg = 'VM %s restarting too fast' % self.name
978 log.error(msg)
979 raise VmError(msg)
980 self.restart_time = tnow
982 def restart(self):
983 """Restart the domain after it has exited.
984 Reuses the domain id and console port.
986 @return: deferred
987 """
988 try:
989 self.restart_check()
990 self.restart_state = STATE_RESTART_BOOTING
991 d = self.construct(self.config)
992 finally:
993 self.restart_state = None
994 return d
996 def configure_backends(self):
997 """Set configuration flags if the vm is a backend for netif or blkif.
998 Configure the backends to use for vbd and vif if specified.
999 """
1000 for c in sxp.children(self.config, 'backend'):
1001 v = sxp.child0(c)
1002 name = sxp.name(v)
1003 if name == 'blkif':
1004 self.blkif_backend = 1
1005 elif name == 'netif':
1006 self.netif_backend = 1
1007 elif name == 'usbif':
1008 self.usbif_backend = 1
1009 else:
1010 raise VmError('invalid backend type:' + str(name))
1012 def configure(self):
1013 """Configure a vm.
1015 @return: deferred - calls callback with vm
1016 """
1017 d = self.create_devices()
1018 d.addCallback(lambda x: self.create_blkif())
1019 d.addCallback(self._configure)
1020 return d
1022 def _configure(self, val):
1023 d = self.configure_fields()
1024 def cbok(results):
1025 return self
1026 def cberr(err):
1027 self.destroy()
1028 return err
1029 d.addCallback(cbok)
1030 d.addErrback(cberr)
1031 return d
1033 def create_blkif(self):
1034 """Create the block device interface (blkif) for the vm.
1035 The vm needs a blkif even if it doesn't have any disks
1036 at creation time, for example when it uses NFS root.
1038 @return: deferred
1039 """
1040 if self.get_devices("vbd") == []:
1041 ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
1042 back = ctrl.getBackendInterface(0)
1043 return back.connect(recreate=self.recreate)
1044 else:
1045 return None
1047 def dom_construct(self, dom, config):
1048 """Construct a vm for an existing domain.
1050 @param dom: domain id
1051 @param config: domain configuration
1052 @return: deferred
1053 """
1054 d = dom_get(dom)
1055 if not d:
1056 raise VmError("Domain not found: %d" % dom)
1057 try:
1058 self.restore = 1
1059 self.setdom(dom)
1060 #self.name = d['name']
1061 self.memory = d['mem_kb']/1024
1062 deferred = self.construct(config)
1063 finally:
1064 self.restore = 0
1065 return deferred
1067 def configure_fields(self):
1068 """Process the vm configuration fields using the registered handlers.
1069 """
1070 dlist = []
1071 index = {}
1072 for field in sxp.children(self.config):
1073 field_name = sxp.name(field)
1074 field_index = index.get(field_name, 0)
1075 field_handler = get_config_handler(field_name)
1076 # Ignore unknown fields. Warn?
1077 if field_handler:
1078 v = field_handler(self, self.config, field, field_index)
1079 append_deferred(dlist, v)
1080 else:
1081 log.warning("Unknown config field %s", field_name)
1082 index[field_name] = field_index + 1
1083 d = defer.DeferredList(dlist, fireOnOneErrback=1)
1084 d.addErrback(dlist_err)
1085 return d
1088 def vm_image_linux(vm, image):
1089 """Create a VM for a linux image.
1091 @param name: vm name
1092 @param memory: vm memory
1093 @param image: image config
1094 @return: vm
1095 """
1096 kernel = sxp.child_value(image, "kernel")
1097 cmdline = ""
1098 ip = sxp.child_value(image, "ip", None)
1099 if ip:
1100 cmdline += " ip=" + ip
1101 root = sxp.child_value(image, "root")
1102 if root:
1103 cmdline += " root=" + root
1104 args = sxp.child_value(image, "args")
1105 if args:
1106 cmdline += " " + args
1107 ramdisk = sxp.child_value(image, "ramdisk", '')
1108 vm.create_domain("linux", kernel, ramdisk, cmdline)
1109 return vm
1111 def vm_image_plan9(vm, image):
1112 """Create a VM for a Plan 9 image.
1114 name vm name
1115 memory vm memory
1116 image image config
1118 returns vm
1119 """
1120 #todo: Same as for linux. Is that right? If so can unify them.
1121 kernel = sxp.child_value(image, "kernel")
1122 cmdline = ""
1123 ip = sxp.child_value(image, "ip", "dhcp")
1124 if ip:
1125 cmdline += "ip=" + ip
1126 root = sxp.child_value(image, "root")
1127 if root:
1128 cmdline += "root=" + root
1129 args = sxp.child_value(image, "args")
1130 if args:
1131 cmdline += " " + args
1132 ramdisk = sxp.child_value(image, "ramdisk", '')
1133 vifs = vm.config_devices("vif")
1134 vm.create_domain("plan9", kernel, ramdisk, cmdline)
1135 return vm
1137 def vm_image_vmx(vm, image):
1138 """Create a VM for the VMX environment.
1140 @param name: vm name
1141 @param memory: vm memory
1142 @param image: image config
1143 @return: vm
1144 """
1145 kernel = sxp.child_value(image, "kernel")
1146 cmdline = ""
1147 ip = sxp.child_value(image, "ip", "dhcp")
1148 if ip:
1149 cmdline += " ip=" + ip
1150 root = sxp.child_value(image, "root")
1151 if root:
1152 cmdline += " root=" + root
1153 args = sxp.child_value(image, "args")
1154 if args:
1155 cmdline += " " + args
1156 ramdisk = sxp.child_value(image, "ramdisk", '')
1157 memmap = sxp.child_value(vm.config, "memmap", '')
1158 memmap = sxp.parse(open(memmap))[0]
1159 from xen.util.memmap import memmap_parse
1160 memmap = memmap_parse(memmap)
1161 vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
1162 vm.is_vmx = 1
1163 return vm
1165 def vm_dev_vif(vm, val, index, change=0):
1166 """Create a virtual network interface (vif).
1168 @param vm: virtual machine
1169 @param val: vif config
1170 @param index: vif index
1171 @return: deferred
1172 """
1173 vif = vm.next_device_index('vif')
1174 vmac = sxp.child_value(val, "mac")
1175 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1176 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1177 recreate = vm.get_device_recreate('vif', index)
1178 defer = ctrl.attachDevice(vif, val, recreate=recreate)
1179 def cbok(dev):
1180 dev.vifctl('up', vmname=vm.name)
1181 dev.setIndex(index)
1182 vm.add_device('vif', dev)
1183 if change:
1184 dev.interfaceChanged()
1185 return dev
1186 defer.addCallback(cbok)
1187 return defer
1189 def vm_dev_usb(vm, val, index):
1190 """Attach the relevant physical ports to the domains' USB interface.
1192 @param vm: virtual machine
1193 @param val: USB interface config
1194 @param index: USB interface index
1195 @return: deferred
1196 """
1197 ctrl = xend.usbif_create(vm.dom, recreate=vm.recreate)
1198 log.debug("Creating USB interface dom=%d", vm.dom)
1199 defer = ctrl.attachDevice(val, recreate=vm.recreate)
1200 def cbok(path):
1201 vm.add_device('usb', val[1][1])
1202 return path
1203 defer.addCallback(cbok)
1204 return defer
1206 def vm_dev_vbd(vm, val, index, change=0):
1207 """Create a virtual block device (vbd).
1209 @param vm: virtual machine
1210 @param val: vbd config
1211 @param index: vbd index
1212 @return: deferred
1213 """
1214 idx = vm.next_device_index('vbd')
1215 uname = sxp.child_value(val, 'uname')
1216 log.debug("Creating vbd dom=%d uname=%s", vm.dom, uname)
1217 ctrl = xend.blkif_create(vm.dom, recreate=vm.recreate)
1218 recreate = vm.get_device_recreate('vbd', index)
1219 defer = ctrl.attachDevice(idx, val, recreate=recreate)
1220 def cbok(dev):
1221 dev.setIndex(index)
1222 vm.add_device('vbd', dev)
1223 if change:
1224 dev.interfaceChanged()
1225 return dev
1226 defer.addCallback(cbok)
1227 return defer
1229 def parse_pci(val):
1230 """Parse a pci field.
1231 """
1232 if isinstance(val, types.StringType):
1233 radix = 10
1234 if val.startswith('0x') or val.startswith('0X'):
1235 radix = 16
1236 v = int(val, radix)
1237 else:
1238 v = val
1239 return v
1241 def vm_dev_pci(vm, val, index, change=0):
1242 """Add a pci device.
1244 @param vm: virtual machine
1245 @param val: device configuration
1246 @param index: device index
1247 @return: 0 on success
1248 """
1249 bus = sxp.child_value(val, 'bus')
1250 if not bus:
1251 raise VmError('pci: Missing bus')
1252 dev = sxp.child_value(val, 'dev')
1253 if not dev:
1254 raise VmError('pci: Missing dev')
1255 func = sxp.child_value(val, 'func')
1256 if not func:
1257 raise VmError('pci: Missing func')
1258 try:
1259 bus = parse_pci(bus)
1260 dev = parse_pci(dev)
1261 func = parse_pci(func)
1262 except:
1263 raise VmError('pci: invalid parameter')
1264 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1265 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1266 func=func, enable=1)
1267 if rc < 0:
1268 #todo non-fatal
1269 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1270 (bus, dev, func))
1271 return rc
1274 def vm_field_ignore(vm, config, val, index):
1275 """Dummy config field handler used for fields with built-in handling.
1277 @param vm: virtual machine
1278 @param config: vm config
1279 @param val: config field
1280 @param index: field index
1281 """
1282 pass
1284 def vm_field_maxmem(vm, config, val, index):
1285 """Configure vm memory limit.
1287 @param vm: virtual machine
1288 @param config: vm config
1289 @param val: config field
1290 @param index: field index
1291 """
1292 maxmem = sxp.child0(val)
1293 if maxmem is None:
1294 maxmem = vm.memory
1295 try:
1296 maxmem = int(maxmem)
1297 except:
1298 raise VmError("invalid maxmem: " + str(maxmem))
1299 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1301 # Register image handlers.
1302 add_image_handler('linux', vm_image_linux)
1303 add_image_handler('plan9', vm_image_plan9)
1304 add_image_handler('vmx', vm_image_vmx)
1306 # Register device handlers.
1307 add_device_handler('vif', vm_dev_vif)
1308 add_device_handler('vbd', vm_dev_vbd)
1309 add_device_handler('pci', vm_dev_pci)
1310 add_device_handler('usb', vm_dev_usb)
1312 # Ignore the fields we already handle.
1313 add_config_handler('name', vm_field_ignore)
1314 add_config_handler('memory', vm_field_ignore)
1315 add_config_handler('cpu', vm_field_ignore)
1316 add_config_handler('cpu_weight', vm_field_ignore)
1317 add_config_handler('console', vm_field_ignore)
1318 add_config_handler('image', vm_field_ignore)
1319 add_config_handler('device', vm_field_ignore)
1320 add_config_handler('backend', vm_field_ignore)
1321 add_config_handler('vcpus', vm_field_ignore)
1323 # Register other config handlers.
1324 add_config_handler('maxmem', vm_field_maxmem)