ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 2694:82d4a403106d

bitkeeper revision 1.1159.122.2 (4177d249OfMUUU4w3P-8H14U7koYXQ)

Remove netbsd domain builder.
author cl349@freefall.cl.cam.ac.uk
date Thu Oct 21 15:14:17 2004 +0000 (2004-10-21)
parents 5acf9e0cea1d
children 5576a0159e82
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 class XendDomainInfo:
292 """Virtual machine object."""
294 """Minimum time between domain restarts in seconds.
295 """
296 MINIMUM_RESTART_TIME = 20
298 def __init__(self):
299 self.recreate = 0
300 self.restore = 0
301 self.config = None
302 self.id = None
303 self.dom = None
304 self.cpu_weight = 1
305 self.start_time = None
306 self.name = None
307 self.memory = None
308 self.image = None
309 self.ramdisk = None
310 self.cmdline = None
311 self.console = None
312 self.devices = {}
313 self.device_index = {}
314 self.configs = []
315 self.info = None
316 self.ipaddrs = []
317 self.blkif_backend = 0
318 self.netif_backend = 0
319 #todo: state: running, suspended
320 self.state = STATE_VM_OK
321 #todo: set to migrate info if migrating
322 self.migrate = None
323 self.restart_mode = RESTART_ONREBOOT
324 self.restart_state = None
325 self.restart_time = None
326 self.console_port = None
328 def setdom(self, dom):
329 """Set the domain id.
331 @param dom: domain id
332 """
333 self.dom = int(dom)
334 self.id = str(dom)
336 def update(self, info):
337 """Update with info from xc.domain_getinfo().
338 """
339 self.info = info
340 self.memory = self.info['mem_kb'] / 1024
342 def __str__(self):
343 s = "domain"
344 s += " id=" + self.id
345 s += " name=" + self.name
346 s += " memory=" + str(self.memory)
347 if self.console:
348 s += " console=" + str(self.console.console_port)
349 if self.image:
350 s += " image=" + self.image
351 s += ""
352 return s
354 __repr__ = __str__
356 def sxpr(self):
357 sxpr = ['domain',
358 ['id', self.id],
359 ['name', self.name],
360 ['memory', self.memory] ]
362 if self.info:
363 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
364 run = (self.info['running'] and 'r') or '-'
365 block = (self.info['blocked'] and 'b') or '-'
366 pause = (self.info['paused'] and 'p') or '-'
367 shut = (self.info['shutdown'] and 's') or '-'
368 crash = (self.info['crashed'] and 'c') or '-'
369 state = run + block + pause + shut + crash
370 sxpr.append(['state', state])
371 if self.info['shutdown']:
372 reason = shutdown_reason(self.info['shutdown_reason'])
373 sxpr.append(['shutdown_reason', reason])
374 sxpr.append(['cpu', self.info['cpu']])
375 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
377 if self.start_time:
378 up_time = time.time() - self.start_time
379 sxpr.append(['up_time', str(up_time) ])
380 sxpr.append(['start_time', str(self.start_time) ])
382 if self.console:
383 sxpr.append(self.console.sxpr())
384 if self.restart_state:
385 sxpr.append(['restart_state', self.restart_state])
386 if self.restart_time:
387 sxpr.append(['restart_time', str(self.restart_time)])
388 if self.config:
389 sxpr.append(['config', self.config])
390 return sxpr
392 def check_name(self, name):
393 """Check if a vm name is valid. Valid names start with a non-digit
394 and contain alphabetic characters, digits, or characters in '_-.:/+'.
395 The same name cannot be used for more than one vm at the same time.
397 @param name: name
398 @raise: VMerror if invalid
399 """
400 if self.recreate: return
401 if name is None or name == '':
402 raise VmError('missing vm name')
403 if name[0] in string.digits:
404 raise VmError('invalid vm name')
405 for c in name:
406 if c in string.digits: continue
407 if c in '_-.:/+': continue
408 if c in string.ascii_letters: continue
409 raise VmError('invalid vm name')
410 dominfo = domain_exists(name)
411 # When creating or rebooting, a domain with my name should not exist.
412 # When restoring, a domain with my name will exist, but it should have
413 # my domain id.
414 if not dominfo:
415 return
416 if dominfo.is_terminated():
417 return
418 if not self.dom or (dominfo.dom != self.dom):
419 raise VmError('vm name clash: ' + name)
421 def construct(self, config):
422 """Construct the vm instance from its configuration.
424 @param config: configuration
425 @return: deferred
426 @raise: VmError on error
427 """
428 # todo - add support for scheduling params?
429 self.config = config
430 try:
431 self.name = sxp.child_value(config, 'name')
432 self.check_name(self.name)
433 try:
434 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
435 except:
436 raise VmError('invalid cpu weight')
437 if self.restore and self.dom:
438 xc.domain_setname(self.dom, self.name)
439 self.memory = int(sxp.child_value(config, 'memory'))
440 if self.memory is None:
441 raise VmError('missing memory size')
442 cpu = sxp.child_value(config, 'cpu')
443 if self.dom and cpu is not None:
444 xc.domain_pincpu(self.dom, int(cpu))
446 self.init_domain()
447 self.configure_console()
448 self.construct_image()
449 self.configure_restart()
450 self.configure_backends()
451 deferred = self.configure()
452 def cberr(err):
453 self.destroy()
454 return err
455 deferred.addErrback(cberr)
456 except StandardError, ex:
457 # Catch errors, cleanup and re-raise.
458 self.destroy()
459 raise
460 return deferred
462 def construct_image(self):
463 """Construct the boot image for the domain.
465 @return vm
466 """
467 image = sxp.child_value(self.config, 'image')
468 if image is None:
469 raise VmError('missing image')
470 image_name = sxp.name(image)
471 if image_name is None:
472 raise VmError('missing image name')
473 image_handler = get_image_handler(image_name)
474 if image_handler is None:
475 raise VmError('unknown image type: ' + image_name)
476 image_handler(self, image)
477 return self
479 def config_devices(self, name):
480 """Get a list of the 'device' nodes of a given type from the config.
482 @param name: device type
483 @type name: string
484 @return: device configs
485 @rtype: list
486 """
487 devices = []
488 for d in sxp.children(self.config, 'device'):
489 dev = sxp.child0(d)
490 if dev is None: continue
491 if name == sxp.name(dev):
492 devices.append(dev)
493 return devices
495 def config_device(self, type, idx):
496 """Get a device config from the device nodes of a given type
497 from the config.
499 @param type: device type
500 @type type: string
501 @param idx: index
502 @type idx: int
503 @return config or None
504 """
505 devs = self.config_devices(type)
506 if 0 <= idx < len(devs):
507 return devs[idx]
508 else:
509 return None
511 def next_device_index(self, type):
512 """Get the next index for a given device type.
514 @param type: device type
515 @type type: string
516 @return device index
517 @rtype: int
518 """
519 idx = self.device_index.get(type, 0)
520 self.device_index[type] = idx + 1
521 return idx
523 def add_device(self, type, dev):
524 """Add a device to a virtual machine.
526 @param type: device type
527 @param dev: device to add
528 """
529 dl = self.devices.get(type, [])
530 dl.append(dev)
531 self.devices[type] = dl
533 def remove_device(self, type, dev):
534 """Remove a device from a virtual machine.
536 @param type: device type
537 @param dev: device
538 """
539 dl = self.devices.get(type, [])
540 if dev in dl:
541 dl.remove(dev)
543 def get_devices(self, type):
544 """Get a list of the devices of a given type.
546 @param type: device type
547 @return: devices
548 """
549 val = self.devices.get(type, [])
550 return val
552 def get_device_by_id(self, type, id):
553 """Get the device with the given id.
555 @param id: device id
556 @return: device or None
557 """
558 dl = self.get_devices(type)
559 for d in dl:
560 if d.getprop('id') == id:
561 return d
562 return None
564 def get_device_by_index(self, type, idx):
565 """Get the device with the given index.
567 @param idx: device index
568 @return: device or None
569 """
570 idx = str(idx)
571 dl = self.get_devices(type)
572 for d in dl:
573 if d.getidx() == idx:
574 return d
575 return None
577 def add_config(self, val):
578 """Add configuration data to a virtual machine.
580 @param val: data to add
581 """
582 self.configs.append(val)
584 def destroy(self):
585 """Completely destroy the vm.
586 """
587 self.cleanup()
588 return self.destroy_domain()
590 def destroy_domain(self):
591 """Destroy the vm's domain.
592 The domain will not finally go away unless all vm
593 devices have been released.
594 """
595 if self.dom is None: return 0
596 self.destroy_console()
597 chan = xend.getDomChannel(self.dom)
598 if chan:
599 log.debug("Closing channel to domain %d", self.dom)
600 chan.close()
601 try:
602 return xc.domain_destroy(dom=self.dom)
603 except Exception, err:
604 log.exception("Domain destroy failed: %s", self.name)
606 def destroy_console(self):
607 if self.console:
608 if self.restart_pending():
609 self.console.deregisterChannel()
610 else:
611 log.debug('Closing console, domain %s', self.id)
612 self.console.close()
614 def cleanup(self):
615 """Cleanup vm resources: release devices.
616 """
617 self.state = STATE_VM_TERMINATED
618 self.release_devices()
620 def is_terminated(self):
621 """Check if a domain has been terminated.
622 """
623 return self.state == STATE_VM_TERMINATED
625 def release_devices(self):
626 """Release all vm devices.
627 """
628 self.release_vifs()
629 self.release_vbds()
631 self.devices = {}
632 self.device_index = {}
633 self.configs = []
634 self.ipaddrs = []
636 def release_vifs(self):
637 """Release vm virtual network devices (vifs).
638 """
639 if self.dom is None: return
640 ctrl = xend.netif_get(self.dom)
641 if ctrl:
642 log.debug("Destroying vifs for domain %d", self.dom)
643 ctrl.destroy()
645 def release_vbds(self):
646 """Release vm virtual block devices (vbds).
647 """
648 if self.dom is None: return
649 ctrl = xend.blkif_get(self.dom)
650 if ctrl:
651 log.debug("Destroying vbds for domain %d", self.dom)
652 ctrl.destroy()
654 def show(self):
655 """Print virtual machine info.
656 """
657 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
658 print "image:"
659 sxp.show(self.image)
660 print
661 for dl in self.devices:
662 for dev in dl:
663 print "device:"
664 sxp.show(dev)
665 print
666 for val in self.configs:
667 print "config:"
668 sxp.show(val)
669 print
670 print "]"
672 def init_domain(self):
673 """Initialize the domain memory.
674 """
675 if self.recreate:
676 return
677 if self.start_time is None:
678 self.start_time = time.time()
679 if self.restore:
680 return
681 dom = self.dom or 0
682 memory = self.memory
683 name = self.name
684 # If the name is over the xen limit, use the end of it.
685 if len(name) > MAX_DOMAIN_NAME:
686 name = name[-MAX_DOMAIN_NAME:]
687 try:
688 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
689 except:
690 raise VmError('invalid cpu')
691 cpu_weight = self.cpu_weight
692 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024,
693 name= name, cpu= cpu, cpu_weight= cpu_weight)
694 if dom <= 0:
695 raise VmError('Creating domain failed: name=%s memory=%d'
696 % (name, memory))
697 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
698 self.setdom(dom)
700 def build_domain(self, ostype, kernel, ramdisk, cmdline):
701 """Build the domain boot image.
702 """
703 if self.recreate or self.restore: return
704 if len(cmdline) >= 256:
705 log.warning('kernel cmdline too long, domain %d', self.dom)
706 dom = self.dom
707 buildfn = getattr(xc, '%s_build' % ostype)
708 flags = 0
709 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
710 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
711 err = buildfn(dom = dom,
712 image = kernel,
713 control_evtchn = self.console.getRemotePort(),
714 cmdline = cmdline,
715 ramdisk = ramdisk,
716 flags = flags)
717 if err != 0:
718 raise VmError('Building domain failed: type=%s dom=%d err=%d'
719 % (ostype, dom, err))
721 def create_domain(self, ostype, kernel, ramdisk, cmdline):
722 """Create a domain. Builds the image but does not configure it.
724 @param ostype: OS type
725 @param kernel: kernel image
726 @param ramdisk: kernel ramdisk
727 @param cmdline: kernel commandline
728 """
729 if not self.recreate:
730 if not os.path.isfile(kernel):
731 raise VmError('Kernel image does not exist: %s' % kernel)
732 if ramdisk and not os.path.isfile(ramdisk):
733 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
734 #self.init_domain()
735 if self.console:
736 self.console.registerChannel()
737 else:
738 self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
739 self.build_domain(ostype, kernel, ramdisk, cmdline)
740 self.image = kernel
741 self.ramdisk = ramdisk
742 self.cmdline = cmdline
744 def create_devices(self):
745 """Create the devices for a vm.
747 @return: Deferred
748 @raise: VmError for invalid devices
749 """
750 dlist = []
751 devices = sxp.children(self.config, 'device')
752 index = {}
753 for d in devices:
754 dev = sxp.child0(d)
755 if dev is None:
756 raise VmError('invalid device')
757 dev_name = sxp.name(dev)
758 dev_index = index.get(dev_name, 0)
759 dev_handler = get_device_handler(dev_name)
760 if dev_handler is None:
761 raise VmError('unknown device type: ' + dev_name)
762 v = dev_handler(self, dev, dev_index)
763 append_deferred(dlist, v)
764 index[dev_name] = dev_index + 1
765 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
766 return deferred
768 def device_create(self, dev_config):
769 """Create a new device.
771 @param dev_config: device configuration
772 @return: deferred
773 """
774 dev_name = sxp.name(dev_config)
775 dev_handler = get_device_handler(dev_name)
776 if dev_handler is None:
777 raise VmError('unknown device type: ' + dev_name)
778 devs = self.get_devices(dev_name)
779 dev_index = len(devs)
780 self.config.append(['device', dev_config])
781 d = dev_handler(self, dev_config, dev_index, change=1)
782 def cbok(dev):
783 return dev.sxpr()
784 d.addCallback(cbok)
785 return d
787 def device_configure(self, dev_config, idx):
788 """Configure an existing device.
790 @param dev_config: device configuration
791 @param idx: device index
792 """
793 type = sxp.name(dev_config)
794 dev = self.get_device_by_index(type, idx)
795 if not dev:
796 raise VmError('invalid device: %s %s' % (type, idx))
797 new_config = dev.configure(dev_config, change=1)
798 devs = self.devices.get(type)
799 index = devs.index(dev)
800 # Patch new config into device configs.
801 dev_configs = self.config_devices(type)
802 old_config = dev_configs[index]
803 dev_configs[index] = new_config
804 # Patch new config into vm config.
805 new_full_config = ['device', new_config]
806 old_full_config = ['device', old_config]
807 old_index = self.config.index(old_full_config)
808 self.config[old_index] = new_full_config
809 return new_config
811 def device_destroy(self, type, idx):
812 """Destroy a device.
814 @param type: device type
815 @param idx: device index
816 """
817 dev = self.get_device_by_index(type, idx)
818 if not dev:
819 raise VmError('invalid device: %s %s' % (type, idx))
820 devs = self.devices.get(type)
821 index = devs.index(dev)
822 dev_config = self.config_device(type, index)
823 if dev_config:
824 self.config.remove(['device', dev_config])
825 dev.destroy(change=1)
826 self.remove_device(type, dev)
828 def configure_memory(self):
829 """Configure vm memory limit.
830 """
831 maxmem = sxp.get_child_value(self.config, "maxmem")
832 if maxmem is None:
833 maxmem = self.memory
834 xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
836 def configure_console(self):
837 """Configure the vm console port.
838 """
839 x = sxp.child_value(self.config, 'console')
840 if x:
841 try:
842 port = int(x)
843 except:
844 raise VmError('invalid console:' + str(x))
845 self.console_port = port
847 def configure_restart(self):
848 """Configure the vm restart mode.
849 """
850 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
851 if r not in restart_modes:
852 raise VmError('invalid restart mode: ' + str(r))
853 self.restart_mode = r;
855 def restart_needed(self, reason):
856 """Determine if the vm needs to be restarted when shutdown
857 for the given reason.
859 @param reason: shutdown reason
860 @return 1 if needs restaert, 0 otherwise
861 """
862 if self.restart_mode == RESTART_NEVER:
863 return 0
864 if self.restart_mode == RESTART_ALWAYS:
865 return 1
866 if self.restart_mode == RESTART_ONREBOOT:
867 return reason == 'reboot'
868 return 0
870 def restart_cancel(self):
871 """Cancel a vm restart.
872 """
873 self.restart_state = None
875 def restarting(self):
876 """Put the vm into restart mode.
877 """
878 self.restart_state = STATE_RESTART_PENDING
880 def restart_pending(self):
881 """Test if the vm has a pending restart.
882 """
883 return self.restart_state == STATE_RESTART_PENDING
885 def restart_check(self):
886 """Check if domain restart is OK.
887 To prevent restart loops, raise an error if it is
888 less than MINIMUM_RESTART_TIME seconds since the last restart.
889 """
890 tnow = time.time()
891 if self.restart_time is not None:
892 tdelta = tnow - self.restart_time
893 if tdelta < self.MINIMUM_RESTART_TIME:
894 self.restart_cancel()
895 msg = 'VM %s restarting too fast' % self.name
896 log.error(msg)
897 raise VmError(msg)
898 self.restart_time = tnow
900 def restart(self):
901 """Restart the domain after it has exited.
902 Reuses the domain id and console port.
904 @return: deferred
905 """
906 try:
907 self.restart_check()
908 self.restart_state = STATE_RESTART_BOOTING
909 d = self.construct(self.config)
910 finally:
911 self.restart_state = None
912 return d
914 def configure_backends(self):
915 """Set configuration flags if the vm is a backend for netif or blkif.
916 Configure the backends to use for vbd and vif if specified.
917 """
918 for c in sxp.children(self.config, 'backend'):
919 v = sxp.child0(c)
920 name = sxp.name(v)
921 if name == 'blkif':
922 self.blkif_backend = 1
923 elif name == 'netif':
924 self.netif_backend = 1
925 else:
926 raise VmError('invalid backend type:' + str(name))
928 def configure(self):
929 """Configure a vm.
931 @return: deferred - calls callback with vm
932 """
933 d = self.create_blkif()
934 d.addCallback(lambda x: self.create_devices())
935 d.addCallback(self._configure)
936 return d
938 def _configure(self, val):
939 d = self.configure_fields()
940 def cbok(results):
941 return self
942 def cberr(err):
943 self.destroy()
944 return err
945 d.addCallback(cbok)
946 d.addErrback(cberr)
947 return d
949 def create_blkif(self):
950 """Create the block device interface (blkif) for the vm.
951 The vm needs a blkif even if it doesn't have any disks
952 at creation time, for example when it uses NFS root.
954 @return: deferred
955 """
956 ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
957 back = ctrl.getBackendInterface(0)
958 return back.connect(recreate=self.recreate)
960 def dom_construct(self, dom, config):
961 """Construct a vm for an existing domain.
963 @param dom: domain id
964 @param config: domain configuration
965 @return: deferred
966 """
967 d = dom_get(dom)
968 if not d:
969 raise VmError("Domain not found: %d" % dom)
970 try:
971 self.restore = 1
972 self.setdom(dom)
973 #self.name = d['name']
974 self.memory = d['mem_kb']/1024
975 deferred = self.construct(config)
976 finally:
977 self.restore = 0
978 return deferred
980 def configure_fields(self):
981 """Process the vm configuration fields using the registered handlers.
982 """
983 dlist = []
984 index = {}
985 for field in sxp.children(self.config):
986 field_name = sxp.name(field)
987 field_index = index.get(field_name, 0)
988 field_handler = get_config_handler(field_name)
989 # Ignore unknown fields. Warn?
990 if field_handler:
991 v = field_handler(self, self.config, field, field_index)
992 append_deferred(dlist, v)
993 else:
994 log.warning("Unknown config field %s", field_name)
995 index[field_name] = field_index + 1
996 d = defer.DeferredList(dlist, fireOnOneErrback=1)
997 return d
1000 def vm_image_linux(vm, image):
1001 """Create a VM for a linux image.
1003 @param name: vm name
1004 @param memory: vm memory
1005 @param image: image config
1006 @return: vm
1007 """
1008 kernel = sxp.child_value(image, "kernel")
1009 cmdline = ""
1010 ip = sxp.child_value(image, "ip", "dhcp")
1011 if ip:
1012 cmdline += " ip=" + ip
1013 root = sxp.child_value(image, "root")
1014 if root:
1015 cmdline += " root=" + root
1016 args = sxp.child_value(image, "args")
1017 if args:
1018 cmdline += " " + args
1019 ramdisk = sxp.child_value(image, "ramdisk", '')
1020 vm.create_domain("linux", kernel, ramdisk, cmdline)
1021 return vm
1023 def vm_dev_vif(vm, val, index, change=0):
1024 """Create a virtual network interface (vif).
1026 @param vm: virtual machine
1027 @param val: vif config
1028 @param index: vif index
1029 @return: deferred
1030 """
1031 vif = vm.next_device_index('vif')
1032 vmac = sxp.child_value(val, "mac")
1033 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1034 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1035 defer = ctrl.attachDevice(vif, val, recreate=vm.recreate)
1036 def cbok(dev):
1037 dev.vifctl('up', vmname=vm.name)
1038 vm.add_device('vif', dev)
1039 if change:
1040 dev.interfaceChanged()
1041 return dev
1042 defer.addCallback(cbok)
1043 return defer
1045 def vm_dev_vbd(vm, val, index, change=0):
1046 """Create a virtual block device (vbd).
1048 @param vm: virtual machine
1049 @param val: vbd config
1050 @param index: vbd index
1051 @return: deferred
1052 """
1053 uname = sxp.child_value(val, 'uname')
1054 if not uname:
1055 raise VmError('vbd: Missing uname')
1056 dev = sxp.child_value(val, 'dev')
1057 if not dev:
1058 raise VmError('vbd: Missing dev')
1059 mode = sxp.child_value(val, 'mode', 'r')
1060 log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
1061 defer = make_disk(vm, val, uname, dev, mode, vm.recreate)
1062 def fn(vbd):
1063 vbd.dev = dev
1064 vbd.uname = uname
1065 vm.add_device('vbd', vbd)
1066 if change:
1067 vbd.interfaceChanged()
1068 return vbd
1069 defer.addCallback(fn)
1070 return defer
1072 def parse_pci(val):
1073 """Parse a pci field.
1074 """
1075 if isinstance(val, types.StringType):
1076 radix = 10
1077 if val.startswith('0x') or val.startswith('0X'):
1078 radix = 16
1079 v = int(val, radix)
1080 else:
1081 v = val
1082 return v
1084 def vm_dev_pci(vm, val, index, change=0):
1085 """Add a pci device.
1087 @param vm: virtual machine
1088 @param val: device configuration
1089 @param index: device index
1090 @return: 0 on success
1091 """
1092 bus = sxp.child_value(val, 'bus')
1093 if not bus:
1094 raise VmError('pci: Missing bus')
1095 dev = sxp.child_value(val, 'dev')
1096 if not dev:
1097 raise VmError('pci: Missing dev')
1098 func = sxp.child_value(val, 'func')
1099 if not func:
1100 raise VmError('pci: Missing func')
1101 try:
1102 bus = parse_pci(bus)
1103 dev = parse_pci(dev)
1104 func = parse_pci(func)
1105 except:
1106 raise VmError('pci: invalid parameter')
1107 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1108 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1109 func=func, enable=1)
1110 if rc < 0:
1111 #todo non-fatal
1112 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1113 (bus, dev, func))
1114 return rc
1117 def vm_field_ignore(vm, config, val, index):
1118 """Dummy config field handler used for fields with built-in handling.
1120 @param vm: virtual machine
1121 @param config: vm config
1122 @param val: config field
1123 @param index: field index
1124 """
1125 pass
1127 def vm_field_maxmem(vm, config, val, index):
1128 """Configure vm memory limit.
1130 @param vm: virtual machine
1131 @param config: vm config
1132 @param val: config field
1133 @param index: field index
1134 """
1135 maxmem = sxp.child0(val)
1136 if maxmem is None:
1137 maxmem = vm.memory
1138 try:
1139 maxmem = int(maxmem)
1140 except:
1141 raise VmError("invalid maxmem: " + str(maxmem))
1142 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1144 # Register image handlers.
1145 add_image_handler('linux', vm_image_linux)
1147 # Register device handlers.
1148 add_device_handler('vif', vm_dev_vif)
1149 add_device_handler('vbd', vm_dev_vbd)
1150 add_device_handler('pci', vm_dev_pci)
1152 # Ignore the fields we already handle.
1153 add_config_handler('name', vm_field_ignore)
1154 add_config_handler('memory', vm_field_ignore)
1155 add_config_handler('cpu', vm_field_ignore)
1156 add_config_handler('cpu_weight', vm_field_ignore)
1157 add_config_handler('console', vm_field_ignore)
1158 add_config_handler('image', vm_field_ignore)
1159 add_config_handler('device', vm_field_ignore)
1160 add_config_handler('backend', vm_field_ignore)
1162 # Register other config handlers.
1163 add_config_handler('maxmem', vm_field_maxmem)