ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 3710:e8b4923ca943

bitkeeper revision 1.1159.212.122 (420813205N6xleek-CEt8_zMptvY9w)

Merge labyrinth.cl.cam.ac.uk:/auto/groups/xeno-xenod/BK/xen-2.0-testing.bk
into labyrinth.cl.cam.ac.uk:/auto/groups/xeno-xenod/BK/xen-unstable.bk
author iap10@labyrinth.cl.cam.ac.uk
date Tue Feb 08 01:17:20 2005 +0000 (2005-02-08)
parents a920f0ced90f d683070e3e97
children f5f2757b3aa2
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.image_handler = None
324 self.is_vmx = 0
325 self.vcpus = 1
327 def setdom(self, dom):
328 """Set the domain id.
330 @param dom: domain id
331 """
332 self.dom = int(dom)
333 self.id = str(dom)
335 def update(self, info):
336 """Update with info from xc.domain_getinfo().
337 """
338 self.info = info
339 self.memory = self.info['mem_kb'] / 1024
341 def __str__(self):
342 s = "domain"
343 s += " id=" + self.id
344 s += " name=" + self.name
345 s += " memory=" + str(self.memory)
346 if self.console:
347 s += " console=" + str(self.console.console_port)
348 if self.image:
349 s += " image=" + self.image
350 s += ""
351 return s
353 __repr__ = __str__
355 def sxpr(self):
356 sxpr = ['domain',
357 ['id', self.id],
358 ['name', self.name],
359 ['memory', self.memory] ]
361 if self.info:
362 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
363 run = (self.info['running'] and 'r') or '-'
364 block = (self.info['blocked'] and 'b') or '-'
365 pause = (self.info['paused'] and 'p') or '-'
366 shut = (self.info['shutdown'] and 's') or '-'
367 crash = (self.info['crashed'] and 'c') or '-'
368 state = run + block + pause + shut + crash
369 sxpr.append(['state', state])
370 if self.info['shutdown']:
371 reason = shutdown_reason(self.info['shutdown_reason'])
372 sxpr.append(['shutdown_reason', reason])
373 sxpr.append(['cpu', self.info['cpu']])
374 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
376 if self.start_time:
377 up_time = time.time() - self.start_time
378 sxpr.append(['up_time', str(up_time) ])
379 sxpr.append(['start_time', str(self.start_time) ])
381 if self.console:
382 sxpr.append(self.console.sxpr())
383 if self.restart_state:
384 sxpr.append(['restart_state', self.restart_state])
385 if self.restart_time:
386 sxpr.append(['restart_time', str(self.restart_time)])
387 devs = self.sxpr_devices()
388 if devs:
389 sxpr.append(devs)
390 if self.config:
391 sxpr.append(['config', self.config])
392 return sxpr
394 def sxpr_devices(self):
395 sxpr = ['devices']
396 for devs in self.devices.values():
397 for dev in devs:
398 if hasattr(dev, 'sxpr'):
399 sxpr.append(dev.sxpr())
400 return sxpr
402 def check_name(self, name):
403 """Check if a vm name is valid. Valid names start with a non-digit
404 and contain alphabetic characters, digits, or characters in '_-.:/+'.
405 The same name cannot be used for more than one vm at the same time.
407 @param name: name
408 @raise: VMerror if invalid
409 """
410 if self.recreate: return
411 if name is None or name == '':
412 raise VmError('missing vm name')
413 if name[0] in string.digits:
414 raise VmError('invalid vm name')
415 for c in name:
416 if c in string.digits: continue
417 if c in '_-.:/+': continue
418 if c in string.ascii_letters: continue
419 raise VmError('invalid vm name')
420 dominfo = domain_exists(name)
421 # When creating or rebooting, a domain with my name should not exist.
422 # When restoring, a domain with my name will exist, but it should have
423 # my domain id.
424 if not dominfo:
425 return
426 if dominfo.is_terminated():
427 return
428 if not self.dom or (dominfo.dom != self.dom):
429 raise VmError('vm name clash: ' + name)
431 def construct(self, config):
432 """Construct the vm instance from its configuration.
434 @param config: configuration
435 @return: deferred
436 @raise: VmError on error
437 """
438 # todo - add support for scheduling params?
439 self.config = config
440 try:
441 self.name = sxp.child_value(config, 'name')
442 self.check_name(self.name)
443 try:
444 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
445 except:
446 raise VmError('invalid cpu weight')
447 self.memory = int(sxp.child_value(config, 'memory'))
448 if self.memory is None:
449 raise VmError('missing memory size')
450 cpu = sxp.child_value(config, 'cpu')
451 if self.recreate and self.dom and cpu is not None:
452 xc.domain_pincpu(self.dom, int(cpu))
453 try:
454 image = sxp.child_value(self.config, 'image')
455 self.vcpus = int(sxp.child_value(image, 'vcpus'))
456 except:
457 raise VmError('invalid vcpus value')
459 self.find_image_handler()
460 self.init_domain()
461 self.configure_console()
462 self.configure_backends()
463 self.construct_image()
464 self.configure_restart()
465 deferred = self.configure()
466 def cberr(err):
467 self.destroy()
468 return err
469 deferred.addErrback(cberr)
470 except StandardError, ex:
471 # Catch errors, cleanup and re-raise.
472 self.destroy()
473 raise
474 return deferred
476 def find_image_handler(self):
477 """Construct the boot image for the domain.
479 @return vm
480 """
481 image = sxp.child_value(self.config, 'image')
482 if image is None:
483 raise VmError('missing image')
484 image_name = sxp.name(image)
485 if image_name is None:
486 raise VmError('missing image name')
487 if image_name == "vmx":
488 self.is_vmx = 1
489 image_handler = get_image_handler(image_name)
490 if image_handler is None:
491 raise VmError('unknown image type: ' + image_name)
492 self.image_handler = image_handler
493 return self
495 def construct_image(self):
496 image = sxp.child_value(self.config, 'image')
497 self.image_handler(self, image)
498 return self
500 def config_devices(self, name):
501 """Get a list of the 'device' nodes of a given type from the config.
503 @param name: device type
504 @type name: string
505 @return: device configs
506 @rtype: list
507 """
508 devices = []
509 for d in sxp.children(self.config, 'device'):
510 dev = sxp.child0(d)
511 if dev is None: continue
512 if name == sxp.name(dev):
513 devices.append(dev)
514 return devices
516 def config_device(self, type, idx):
517 """Get a device config from the device nodes of a given type
518 from the config.
520 @param type: device type
521 @type type: string
522 @param idx: index
523 @type idx: int
524 @return config or None
525 """
526 devs = self.config_devices(type)
527 if 0 <= idx < len(devs):
528 return devs[idx]
529 else:
530 return None
532 def next_device_index(self, type):
533 """Get the next index for a given device type.
535 @param type: device type
536 @type type: string
537 @return device index
538 @rtype: int
539 """
540 idx = self.device_index.get(type, 0)
541 self.device_index[type] = idx + 1
542 return idx
544 def add_device(self, type, dev):
545 """Add a device to a virtual machine.
547 @param type: device type
548 @param dev: device to add
549 """
550 dl = self.devices.get(type, [])
551 dl.append(dev)
552 self.devices[type] = dl
554 def remove_device(self, type, dev):
555 """Remove a device from a virtual machine.
557 @param type: device type
558 @param dev: device
559 """
560 dl = self.devices.get(type, [])
561 if dev in dl:
562 dl.remove(dev)
564 def get_devices(self, type):
565 """Get a list of the devices of a given type.
567 @param type: device type
568 @return: devices
569 """
570 val = self.devices.get(type, [])
571 return val
573 def get_device_by_id(self, type, id):
574 """Get the device with the given id.
576 @param id: device id
577 @return: device or None
578 """
579 dl = self.get_devices(type)
580 for d in dl:
581 if d.getprop('id') == id:
582 return d
583 return None
585 def get_device_by_index(self, type, idx):
586 """Get the device with the given index.
588 @param idx: device index
589 @return: device or None
590 """
591 idx = str(idx)
592 dl = self.get_devices(type)
593 for d in dl:
594 if d.getidx() == idx:
595 return d
596 return None
598 def get_device_savedinfo(self, type, index):
599 val = None
600 if self.savedinfo is None:
601 return val
602 index = str(index)
603 devinfo = sxp.child(self.savedinfo, 'devices')
604 if devinfo is None:
605 return val
606 for d in sxp.children(devinfo, type):
607 dindex = sxp.child_value(d, 'index')
608 if dindex is None: continue
609 if str(dindex) == index:
610 val = d
611 break
612 return val
614 def get_device_recreate(self, type, index):
615 return self.get_device_savedinfo(type, index) or self.recreate
617 def add_config(self, val):
618 """Add configuration data to a virtual machine.
620 @param val: data to add
621 """
622 self.configs.append(val)
624 def destroy(self):
625 """Completely destroy the vm.
626 """
627 self.cleanup()
628 return self.destroy_domain()
630 def destroy_domain(self):
631 """Destroy the vm's domain.
632 The domain will not finally go away unless all vm
633 devices have been released.
634 """
635 if self.dom is None: return 0
636 self.destroy_console()
637 chan = xend.getDomChannel(self.dom)
638 if chan:
639 log.debug("Closing channel to domain %d", self.dom)
640 chan.close()
641 try:
642 return xc.domain_destroy(dom=self.dom)
643 except Exception, err:
644 log.exception("Domain destroy failed: %s", self.name)
646 def destroy_console(self):
647 if self.console:
648 if self.restart_pending():
649 self.console.deregisterChannel()
650 else:
651 log.debug('Closing console, domain %s', self.id)
652 self.console.close()
654 def cleanup(self):
655 """Cleanup vm resources: release devices.
656 """
657 self.state = STATE_VM_TERMINATED
658 self.release_devices()
660 def is_terminated(self):
661 """Check if a domain has been terminated.
662 """
663 return self.state == STATE_VM_TERMINATED
665 def release_devices(self):
666 """Release all vm devices.
667 """
668 self.release_vifs()
669 self.release_vbds()
670 self.release_usbifs()
672 self.devices = {}
673 self.device_index = {}
674 self.configs = []
675 self.ipaddrs = []
677 def release_vifs(self):
678 """Release vm virtual network devices (vifs).
679 """
680 if self.dom is None: return
681 ctrl = xend.netif_get(self.dom)
682 if ctrl:
683 log.debug("Destroying vifs for domain %d", self.dom)
684 ctrl.destroy()
686 def release_vbds(self):
687 """Release vm virtual block devices (vbds).
688 """
689 if self.dom is None: return
690 ctrl = xend.blkif_get(self.dom)
691 if ctrl:
692 log.debug("Destroying vbds for domain %d", self.dom)
693 ctrl.destroy()
695 def release_usbifs(self):
696 """Release vm virtual USB devices (usbifs).
697 """
698 if self.dom is None: return
699 ctrl = xend.usbif_get(self.dom)
700 if ctrl:
701 log.debug("Destroying usbifs for domain %d", self.dom)
702 ctrl.destroy()
704 def show(self):
705 """Print virtual machine info.
706 """
707 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
708 print "image:"
709 sxp.show(self.image)
710 print
711 for dl in self.devices:
712 for dev in dl:
713 print "device:"
714 sxp.show(dev)
715 print
716 for val in self.configs:
717 print "config:"
718 sxp.show(val)
719 print
720 print "]"
722 def init_domain(self):
723 """Initialize the domain memory.
724 """
725 if self.recreate:
726 return
727 if self.start_time is None:
728 self.start_time = time.time()
729 if self.restore:
730 return
731 dom = self.dom or 0
732 memory = self.memory
733 name = self.name
734 # If the name is over the xen limit, use the end of it.
735 if len(name) > MAX_DOMAIN_NAME:
736 name = name[-MAX_DOMAIN_NAME:]
737 try:
738 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
739 except:
740 raise VmError('invalid cpu')
741 cpu_weight = self.cpu_weight
742 memory = memory * 1024 + self.pgtable_size(memory)
743 dom = xc.domain_create(dom= dom, mem_kb= memory,
744 cpu= cpu, cpu_weight= cpu_weight)
745 if dom <= 0:
746 raise VmError('Creating domain failed: name=%s memory=%d'
747 % (name, memory))
748 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
749 self.setdom(dom)
751 def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
752 """Build the domain boot image.
753 """
754 if self.recreate or self.restore: return
755 if not os.path.isfile(kernel):
756 raise VmError('Kernel image does not exist: %s' % kernel)
757 if ramdisk and not os.path.isfile(ramdisk):
758 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
759 if len(cmdline) >= 256:
760 log.warning('kernel cmdline too long, domain %d', self.dom)
761 dom = self.dom
762 buildfn = getattr(xc, '%s_build' % ostype)
763 flags = 0
764 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
765 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
766 if ostype == "vmx":
767 err = buildfn(dom = dom,
768 image = kernel,
769 control_evtchn = 0,
770 memsize = self.memory,
771 memmap = memmap,
772 cmdline = cmdline,
773 ramdisk = ramdisk,
774 flags = flags)
775 else:
776 log.warning('building dom with %d vcpus', self.vcpus)
777 err = buildfn(dom = dom,
778 image = kernel,
779 control_evtchn = self.console.getRemotePort(),
780 cmdline = cmdline,
781 ramdisk = ramdisk,
782 flags = flags,
783 vcpus = self.vcpus)
784 if err != 0:
785 raise VmError('Building domain failed: type=%s dom=%d err=%d'
786 % (ostype, dom, err))
788 def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
789 """Create a domain. Builds the image but does not configure it.
791 @param ostype: OS type
792 @param kernel: kernel image
793 @param ramdisk: kernel ramdisk
794 @param cmdline: kernel commandline
795 """
797 self.create_channel()
798 if self.console:
799 self.console.registerChannel()
800 else:
801 self.console = xendConsole.console_create(
802 self.dom, console_port=self.console_port)
803 self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
804 self.image = kernel
805 self.ramdisk = ramdisk
806 self.cmdline = cmdline
808 def create_channel(self):
809 """Create the channel to the domain.
810 If saved info is available recreate the channel using the saved ports.
812 @return: channel
813 """
814 local = 0
815 remote = 1
816 if self.savedinfo:
817 consinfo = sxp.child(self.savedinfo, "console")
818 if consinfo:
819 local = int(sxp.child_value(consinfo, "local_port", 0))
820 remote = int(sxp.child_value(consinfo, "remote_port", 1))
821 return xend.createDomChannel(self.dom, local_port=local,
822 remote_port=remote)
824 def create_devices(self):
825 """Create the devices for a vm.
827 @return: Deferred
828 @raise: VmError for invalid devices
829 """
830 dlist = []
831 devices = sxp.children(self.config, 'device')
832 index = {}
833 for d in devices:
834 dev = sxp.child0(d)
835 if dev is None:
836 raise VmError('invalid device')
837 dev_name = sxp.name(dev)
838 dev_index = index.get(dev_name, 0)
839 dev_handler = get_device_handler(dev_name)
840 if dev_handler is None:
841 raise VmError('unknown device type: ' + dev_name)
842 v = dev_handler(self, dev, dev_index)
843 append_deferred(dlist, v)
844 index[dev_name] = dev_index + 1
845 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
846 deferred.addErrback(dlist_err)
847 if self.is_vmx:
848 device_model = sxp.child_value(self.config, 'device_model')
849 device_config = sxp.child_value(self.config, 'device_config')
850 memory = sxp.child_value(self.config, "memory")
851 # Create an event channel
852 device_channel = channel.eventChannel(0, self.dom)
853 # Fork and exec device_model -f device_config <port>
854 os.system(device_model
855 + " -f %s" % device_config
856 + " -d %d" % self.dom
857 + " -p %d" % device_channel['port1']
858 + " -m %s &" % memory)
859 return deferred
861 def device_create(self, dev_config):
862 """Create a new device.
864 @param dev_config: device configuration
865 @return: deferred
866 """
867 dev_name = sxp.name(dev_config)
868 dev_handler = get_device_handler(dev_name)
869 if dev_handler is None:
870 raise VmError('unknown device type: ' + dev_name)
871 devs = self.get_devices(dev_name)
872 dev_index = len(devs)
873 self.config.append(['device', dev_config])
874 d = dev_handler(self, dev_config, dev_index, change=1)
875 def cbok(dev):
876 return dev.sxpr()
877 d.addCallback(cbok)
878 return d
880 def device_configure(self, dev_config, idx):
881 """Configure an existing device.
883 @param dev_config: device configuration
884 @param idx: device index
885 """
886 type = sxp.name(dev_config)
887 dev = self.get_device_by_index(type, idx)
888 if not dev:
889 raise VmError('invalid device: %s %s' % (type, idx))
890 new_config = dev.configure(dev_config, change=1)
891 devs = self.devices.get(type)
892 index = devs.index(dev)
893 # Patch new config into device configs.
894 dev_configs = self.config_devices(type)
895 old_config = dev_configs[index]
896 dev_configs[index] = new_config
897 # Patch new config into vm config.
898 new_full_config = ['device', new_config]
899 old_full_config = ['device', old_config]
900 old_index = self.config.index(old_full_config)
901 self.config[old_index] = new_full_config
902 return new_config
904 def device_destroy(self, type, idx):
905 """Destroy a device.
907 @param type: device type
908 @param idx: device index
909 """
910 dev = self.get_device_by_index(type, idx)
911 if not dev:
912 raise VmError('invalid device: %s %s' % (type, idx))
913 devs = self.devices.get(type)
914 index = devs.index(dev)
915 dev_config = self.config_device(type, index)
916 if dev_config:
917 self.config.remove(['device', dev_config])
918 dev.destroy(change=1)
919 self.remove_device(type, dev)
921 def configure_memory(self):
922 """Configure vm memory limit.
923 """
924 maxmem = sxp.child_value(self.config, "maxmem")
925 if maxmem is None:
926 maxmem = self.memory
927 xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
929 def configure_console(self):
930 """Configure the vm console port.
931 """
932 x = sxp.child_value(self.config, 'console')
933 if x:
934 try:
935 port = int(x)
936 except:
937 raise VmError('invalid console:' + str(x))
938 self.console_port = port
940 def configure_restart(self):
941 """Configure the vm restart mode.
942 """
943 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
944 if r not in restart_modes:
945 raise VmError('invalid restart mode: ' + str(r))
946 self.restart_mode = r;
948 def restart_needed(self, reason):
949 """Determine if the vm needs to be restarted when shutdown
950 for the given reason.
952 @param reason: shutdown reason
953 @return 1 if needs restaert, 0 otherwise
954 """
955 if self.restart_mode == RESTART_NEVER:
956 return 0
957 if self.restart_mode == RESTART_ALWAYS:
958 return 1
959 if self.restart_mode == RESTART_ONREBOOT:
960 return reason == 'reboot'
961 return 0
963 def restart_cancel(self):
964 """Cancel a vm restart.
965 """
966 self.restart_state = None
968 def restarting(self):
969 """Put the vm into restart mode.
970 """
971 self.restart_state = STATE_RESTART_PENDING
973 def restart_pending(self):
974 """Test if the vm has a pending restart.
975 """
976 return self.restart_state == STATE_RESTART_PENDING
978 def restart_check(self):
979 """Check if domain restart is OK.
980 To prevent restart loops, raise an error if it is
981 less than MINIMUM_RESTART_TIME seconds since the last restart.
982 """
983 tnow = time.time()
984 if self.restart_time is not None:
985 tdelta = tnow - self.restart_time
986 if tdelta < self.MINIMUM_RESTART_TIME:
987 self.restart_cancel()
988 msg = 'VM %s restarting too fast' % self.name
989 log.error(msg)
990 raise VmError(msg)
991 self.restart_time = tnow
993 def restart(self):
994 """Restart the domain after it has exited.
995 Reuses the domain id and console port.
997 @return: deferred
998 """
999 try:
1000 self.restart_check()
1001 self.restart_state = STATE_RESTART_BOOTING
1002 d = self.construct(self.config)
1003 finally:
1004 self.restart_state = None
1005 return d
1007 def configure_backends(self):
1008 """Set configuration flags if the vm is a backend for netif or blkif.
1009 Configure the backends to use for vbd and vif if specified.
1010 """
1011 for c in sxp.children(self.config, 'backend'):
1012 v = sxp.child0(c)
1013 name = sxp.name(v)
1014 if name == 'blkif':
1015 self.blkif_backend = 1
1016 elif name == 'netif':
1017 self.netif_backend = 1
1018 elif name == 'usbif':
1019 self.usbif_backend = 1
1020 else:
1021 raise VmError('invalid backend type:' + str(name))
1023 def configure(self):
1024 """Configure a vm.
1026 @return: deferred - calls callback with vm
1027 """
1028 d = self.create_devices()
1029 d.addCallback(lambda x: self.create_blkif())
1030 d.addCallback(self._configure)
1031 return d
1033 def _configure(self, val):
1034 d = self.configure_fields()
1035 def cbok(results):
1036 return self
1037 def cberr(err):
1038 self.destroy()
1039 return err
1040 d.addCallback(cbok)
1041 d.addErrback(cberr)
1042 return d
1044 def create_blkif(self):
1045 """Create the block device interface (blkif) for the vm.
1046 The vm needs a blkif even if it doesn't have any disks
1047 at creation time, for example when it uses NFS root.
1049 @return: deferred
1050 """
1051 if self.get_devices("vbd") == []:
1052 ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
1053 back = ctrl.getBackendInterface(0)
1054 return back.connect(recreate=self.recreate)
1055 else:
1056 return None
1058 def dom_construct(self, dom, config):
1059 """Construct a vm for an existing domain.
1061 @param dom: domain id
1062 @param config: domain configuration
1063 @return: deferred
1064 """
1065 d = dom_get(dom)
1066 if not d:
1067 raise VmError("Domain not found: %d" % dom)
1068 try:
1069 self.restore = 1
1070 self.setdom(dom)
1071 #self.name = d['name']
1072 self.memory = d['mem_kb']/1024
1073 deferred = self.construct(config)
1074 finally:
1075 self.restore = 0
1076 return deferred
1078 def configure_fields(self):
1079 """Process the vm configuration fields using the registered handlers.
1080 """
1081 dlist = []
1082 index = {}
1083 for field in sxp.children(self.config):
1084 field_name = sxp.name(field)
1085 field_index = index.get(field_name, 0)
1086 field_handler = get_config_handler(field_name)
1087 # Ignore unknown fields. Warn?
1088 if field_handler:
1089 v = field_handler(self, self.config, field, field_index)
1090 append_deferred(dlist, v)
1091 else:
1092 log.warning("Unknown config field %s", field_name)
1093 index[field_name] = field_index + 1
1094 d = defer.DeferredList(dlist, fireOnOneErrback=1)
1095 d.addErrback(dlist_err)
1096 return d
1098 def pgtable_size(self, memory):
1099 """Return the size of memory needed for 1:1 page tables for physical
1100 mode.
1102 @param memory: size in MB
1103 @return size in KB
1104 """
1105 if self.is_vmx:
1106 # Logic x86-32 specific.
1107 # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
1108 return (1 + ((memory + 3) >> 2)) * 4
1109 return 0
1111 def vm_image_linux(vm, image):
1112 """Create a VM for a linux image.
1114 @param name: vm name
1115 @param memory: vm memory
1116 @param image: image config
1117 @return: vm
1118 """
1119 kernel = sxp.child_value(image, "kernel")
1120 cmdline = ""
1121 ip = sxp.child_value(image, "ip", None)
1122 if ip:
1123 cmdline += " ip=" + ip
1124 root = sxp.child_value(image, "root")
1125 if root:
1126 cmdline += " root=" + root
1127 args = sxp.child_value(image, "args")
1128 if args:
1129 cmdline += " " + args
1130 ramdisk = sxp.child_value(image, "ramdisk", '')
1131 vm.create_domain("linux", kernel, ramdisk, cmdline)
1132 return vm
1134 def vm_image_plan9(vm, image):
1135 """Create a VM for a Plan 9 image.
1137 name vm name
1138 memory vm memory
1139 image image config
1141 returns vm
1142 """
1143 #todo: Same as for linux. Is that right? If so can unify them.
1144 kernel = sxp.child_value(image, "kernel")
1145 cmdline = ""
1146 ip = sxp.child_value(image, "ip", "dhcp")
1147 if ip:
1148 cmdline += "ip=" + ip
1149 root = sxp.child_value(image, "root")
1150 if root:
1151 cmdline += "root=" + root
1152 args = sxp.child_value(image, "args")
1153 if args:
1154 cmdline += " " + args
1155 ramdisk = sxp.child_value(image, "ramdisk", '')
1156 vifs = vm.config_devices("vif")
1157 vm.create_domain("plan9", kernel, ramdisk, cmdline)
1158 return vm
1160 def vm_image_vmx(vm, image):
1161 """Create a VM for the VMX environment.
1163 @param name: vm name
1164 @param memory: vm memory
1165 @param image: image config
1166 @return: vm
1167 """
1168 kernel = sxp.child_value(image, "kernel")
1169 cmdline = ""
1170 ip = sxp.child_value(image, "ip", "dhcp")
1171 if ip:
1172 cmdline += " ip=" + ip
1173 root = sxp.child_value(image, "root")
1174 if root:
1175 cmdline += " root=" + root
1176 args = sxp.child_value(image, "args")
1177 if args:
1178 cmdline += " " + args
1179 ramdisk = sxp.child_value(image, "ramdisk", '')
1180 memmap = sxp.child_value(vm.config, "memmap", '')
1181 memmap = sxp.parse(open(memmap))[0]
1182 from xen.util.memmap import memmap_parse
1183 memmap = memmap_parse(memmap)
1184 vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
1185 return vm
1187 def vm_dev_vif(vm, val, index, change=0):
1188 """Create a virtual network interface (vif).
1190 @param vm: virtual machine
1191 @param val: vif config
1192 @param index: vif index
1193 @return: deferred
1194 """
1195 vif = vm.next_device_index('vif')
1196 vmac = sxp.child_value(val, "mac")
1197 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1198 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1199 recreate = vm.get_device_recreate('vif', index)
1200 defer = ctrl.attachDevice(vif, val, recreate=recreate)
1201 def cbok(dev):
1202 dev.vifctl('up', vmname=vm.name)
1203 dev.setIndex(index)
1204 vm.add_device('vif', dev)
1205 if change:
1206 dev.interfaceChanged()
1207 return dev
1208 defer.addCallback(cbok)
1209 return defer
1211 def vm_dev_usb(vm, val, index):
1212 """Attach the relevant physical ports to the domains' USB interface.
1214 @param vm: virtual machine
1215 @param val: USB interface config
1216 @param index: USB interface index
1217 @return: deferred
1218 """
1219 ctrl = xend.usbif_create(vm.dom, recreate=vm.recreate)
1220 log.debug("Creating USB interface dom=%d", vm.dom)
1221 defer = ctrl.attachDevice(val, recreate=vm.recreate)
1222 def cbok(path):
1223 vm.add_device('usb', val[1][1])
1224 return path
1225 defer.addCallback(cbok)
1226 return defer
1228 def vm_dev_vbd(vm, val, index, change=0):
1229 """Create a virtual block device (vbd).
1231 @param vm: virtual machine
1232 @param val: vbd config
1233 @param index: vbd index
1234 @return: deferred
1235 """
1236 idx = vm.next_device_index('vbd')
1237 uname = sxp.child_value(val, 'uname')
1238 log.debug("Creating vbd dom=%d uname=%s", vm.dom, uname)
1239 ctrl = xend.blkif_create(vm.dom, recreate=vm.recreate)
1240 recreate = vm.get_device_recreate('vbd', index)
1241 defer = ctrl.attachDevice(idx, val, recreate=recreate)
1242 def cbok(dev):
1243 dev.setIndex(index)
1244 vm.add_device('vbd', dev)
1245 if change:
1246 dev.interfaceChanged()
1247 return dev
1248 defer.addCallback(cbok)
1249 return defer
1251 def parse_pci(val):
1252 """Parse a pci field.
1253 """
1254 if isinstance(val, types.StringType):
1255 radix = 10
1256 if val.startswith('0x') or val.startswith('0X'):
1257 radix = 16
1258 v = int(val, radix)
1259 else:
1260 v = val
1261 return v
1263 def vm_dev_pci(vm, val, index, change=0):
1264 """Add a pci device.
1266 @param vm: virtual machine
1267 @param val: device configuration
1268 @param index: device index
1269 @return: 0 on success
1270 """
1271 bus = sxp.child_value(val, 'bus')
1272 if not bus:
1273 raise VmError('pci: Missing bus')
1274 dev = sxp.child_value(val, 'dev')
1275 if not dev:
1276 raise VmError('pci: Missing dev')
1277 func = sxp.child_value(val, 'func')
1278 if not func:
1279 raise VmError('pci: Missing func')
1280 try:
1281 bus = parse_pci(bus)
1282 dev = parse_pci(dev)
1283 func = parse_pci(func)
1284 except:
1285 raise VmError('pci: invalid parameter')
1286 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1287 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1288 func=func, enable=1)
1289 if rc < 0:
1290 #todo non-fatal
1291 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1292 (bus, dev, func))
1293 return rc
1296 def vm_field_ignore(vm, config, val, index):
1297 """Dummy config field handler used for fields with built-in handling.
1299 @param vm: virtual machine
1300 @param config: vm config
1301 @param val: config field
1302 @param index: field index
1303 """
1304 pass
1306 def vm_field_maxmem(vm, config, val, index):
1307 """Configure vm memory limit.
1309 @param vm: virtual machine
1310 @param config: vm config
1311 @param val: config field
1312 @param index: field index
1313 """
1314 maxmem = sxp.child0(val)
1315 if maxmem is None:
1316 maxmem = vm.memory
1317 try:
1318 maxmem = int(maxmem)
1319 except:
1320 raise VmError("invalid maxmem: " + str(maxmem))
1321 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1323 # Register image handlers.
1324 add_image_handler('linux', vm_image_linux)
1325 add_image_handler('plan9', vm_image_plan9)
1326 add_image_handler('vmx', vm_image_vmx)
1328 # Register device handlers.
1329 add_device_handler('vif', vm_dev_vif)
1330 add_device_handler('vbd', vm_dev_vbd)
1331 add_device_handler('pci', vm_dev_pci)
1332 add_device_handler('usb', vm_dev_usb)
1334 # Ignore the fields we already handle.
1335 add_config_handler('name', vm_field_ignore)
1336 add_config_handler('memory', vm_field_ignore)
1337 add_config_handler('cpu', vm_field_ignore)
1338 add_config_handler('cpu_weight', vm_field_ignore)
1339 add_config_handler('console', vm_field_ignore)
1340 add_config_handler('restart', vm_field_ignore)
1341 add_config_handler('image', vm_field_ignore)
1342 add_config_handler('device', vm_field_ignore)
1343 add_config_handler('backend', vm_field_ignore)
1344 add_config_handler('vcpus', vm_field_ignore)
1346 # Register other config handlers.
1347 add_config_handler('maxmem', vm_field_maxmem)