debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 2646:b44a94edc865

bitkeeper revision 1.1159.103.1 (4163f41dBS1Bak4UfPFZYlBrHnut8w)

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