direct-io.hg

view tools/python/xen/xend/XendDomainInfo.py @ 2290:709199bb8055

bitkeeper revision 1.1159.1.87 (412495f853g9qdFCK3fe-wp3blfTlA)

Trap error in cpu value from config.
author mjw@wray-m-3.hpl.hp.com
date Thu Aug 19 11:58:48 2004 +0000 (2004-08-19)
parents a6d73a771367
children 97a16adf6fc1
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
19 #defer.Deferred.debug = 1
21 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
22 import xen.util.ip
23 from xen.util.ip import _readline, _readlines
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 """Flag for a block device backend domain."""
38 SIF_BLK_BE_DOMAIN = (1<<4)
40 """Flag for a net device backend domain."""
41 SIF_NET_BE_DOMAIN = (1<<5)
43 """Shutdown code for poweroff."""
44 DOMAIN_POWEROFF = 0
45 """Shutdown code for reboot."""
46 DOMAIN_REBOOT = 1
47 """Shutdown code for suspend."""
48 DOMAIN_SUSPEND = 2
50 """Map shutdown codes to strings."""
51 shutdown_reasons = {
52 DOMAIN_POWEROFF: "poweroff",
53 DOMAIN_REBOOT : "reboot",
54 DOMAIN_SUSPEND : "suspend" }
56 RESTART_ALWAYS = 'always'
57 RESTART_ONREBOOT = 'onreboot'
58 RESTART_NEVER = 'never'
60 restart_modes = [
61 RESTART_ALWAYS,
62 RESTART_ONREBOOT,
63 RESTART_NEVER,
64 ]
66 STATE_RESTART_PENDING = 'pending'
67 STATE_RESTART_BOOTING = 'booting'
69 STATE_VM_OK = "ok"
70 STATE_VM_TERMINATED = "terminated"
73 def domain_exists(name):
74 # See comment in XendDomain constructor.
75 xd = get_component('xen.xend.XendDomain')
76 return xd.domain_exists(name)
78 def shutdown_reason(code):
79 """Get a shutdown reason from a code.
81 @param code: shutdown code
82 @type code: int
83 @return: shutdown reason
84 @rtype: string
85 """
86 return shutdown_reasons.get(code, "?")
88 def blkdev_name_to_number(name):
89 """Take the given textual block-device name (e.g., '/dev/sda1',
90 'hda') and return the device number used by the OS. """
92 if not re.match( '^/dev/', name ):
93 n = '/dev/' + name
94 else:
95 n = name
97 try:
98 return os.stat(n).st_rdev
99 except:
100 pass
102 # see if this is a hex device number
103 if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
104 return string.atoi(name,16)
106 return None
108 def lookup_raw_partn(name):
109 """Take the given block-device name (e.g., '/dev/sda1', 'hda')
110 and return a dictionary { device, start_sector,
111 nr_sectors, type }
112 device: Device number of the given partition
113 start_sector: Index of first sector of the partition
114 nr_sectors: Number of sectors comprising this partition
115 type: 'Disk' or identifying name for partition type
116 """
118 n = blkdev_name_to_number(name)
119 if n:
120 return [ { 'device' : n,
121 'start_sector' : long(0),
122 'nr_sectors' : long(1L<<63),
123 'type' : 'Disk' } ]
124 else:
125 return None
127 def lookup_disk_uname(uname):
128 """Lookup a list of segments for a physical device.
129 uname [string]: name of the device in the format \'phy:dev\' for a physical device
130 returns [list of dicts]: list of extents that make up the named device
131 """
132 ( type, d_name ) = string.split( uname, ':' )
134 if type == "phy":
135 segments = lookup_raw_partn( d_name )
136 else:
137 segments = None
138 return segments
140 def make_disk(vm, config, uname, dev, mode, recreate=0):
141 """Create a virtual disk device for a domain.
143 @param vm: vm
144 @param uname: device to export
145 @param dev: device name in domain
146 @param mode: read/write mode
147 @param recreate: recreate flag (after xend restart)
148 @return: deferred
149 """
150 segments = lookup_disk_uname(uname)
151 if not segments:
152 raise VmError("vbd: Segments not found: uname=%s" % uname)
153 if len(segments) > 1:
154 raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
155 segment = segments[0]
156 vdev = blkdev_name_to_number(dev)
157 ctrl = xend.blkif_create(vm.dom, recreate=recreate)
158 return ctrl.attachDevice(config, vdev, mode, segment, recreate=recreate)
160 def vif_up(iplist):
161 """send an unsolicited ARP reply for all non link-local IP addresses.
163 iplist IP addresses
164 """
166 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
168 def get_ip_nonlocal_bind():
169 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
171 def set_ip_nonlocal_bind(v):
172 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
174 def link_local(ip):
175 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
177 def arping(ip, gw):
178 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
179 print cmd
180 os.system(cmd)
182 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
183 nlb = get_ip_nonlocal_bind()
184 if not nlb: set_ip_nonlocal_bind(1)
185 try:
186 for ip in iplist:
187 if not link_local(ip):
188 arping(ip, gateway)
189 finally:
190 if not nlb: set_ip_nonlocal_bind(0)
192 config_handlers = {}
194 def add_config_handler(name, h):
195 """Add a handler for a config field.
197 @param name: field name
198 @param h: handler: fn(vm, config, field, index)
199 """
200 config_handlers[name] = h
202 def get_config_handler(name):
203 """Get a handler for a config field.
205 returns handler or None
206 """
207 return config_handlers.get(name)
209 """Table of handlers for virtual machine images.
210 Indexed by image type.
211 """
212 image_handlers = {}
214 def add_image_handler(name, h):
215 """Add a handler for an image type
216 @param name: image type
217 @param h: handler: fn(config, name, memory, image)
218 """
219 image_handlers[name] = h
221 def get_image_handler(name):
222 """Get the handler for an image type.
223 @param name: image type
224 @return: handler or None
225 """
226 return image_handlers.get(name)
228 """Table of handlers for devices.
229 Indexed by device type.
230 """
231 device_handlers = {}
233 def add_device_handler(name, h):
234 """Add a handler for a device type.
236 @param name: device type
237 @param h: handler: fn(vm, dev)
238 """
239 device_handlers[name] = h
241 def get_device_handler(name):
242 """Get the handler for a device type.
244 @param name : device type
245 @return; handler or None
246 """
247 return device_handlers.get(name)
249 def vm_create(config):
250 """Create a VM from a configuration.
251 If a vm has been partially created and there is an error it
252 is destroyed.
254 @param config configuration
255 @return: Deferred
256 @raise: VmError for invalid configuration
257 """
258 vm = XendDomainInfo()
259 return vm.construct(config)
261 def vm_recreate(savedinfo, info):
262 """Create the VM object for an existing domain.
264 @param savedinfo: saved info from the domain DB
265 @type savedinfo: sxpr
266 @param info: domain info from xc
267 @type info: xc domain dict
268 @return: deferred
269 """
270 vm = XendDomainInfo()
271 vm.recreate = 1
272 vm.setdom(info['dom'])
273 vm.name = info['name']
274 vm.memory = info['mem_kb']/1024
275 start_time = sxp.child_value(savedinfo, 'start_time')
276 if start_time is not None:
277 vm.start_time = float(start_time)
278 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
279 restart_time = sxp.child_value(savedinfo, 'restart_time')
280 if restart_time is not None:
281 vm.restart_time = float(restart_time)
282 config = sxp.child_value(savedinfo, 'config')
283 if config:
284 d = vm.construct(config)
285 else:
286 d = defer.Deferred()
287 d.callback(vm)
288 vm.recreate = 0
289 return d
291 def vm_restore(src, progress=0):
292 """Restore a VM from a disk image.
294 src saved state to restore
295 progress progress reporting flag
296 returns deferred
297 raises VmError for invalid configuration
298 """
299 vm = XendDomainInfo()
300 ostype = "linux" #todo Set from somewhere (store in the src?).
301 restorefn = getattr(xc, "%s_restore" % ostype)
302 d = restorefn(state_file=src, progress=progress)
303 dom = int(d['dom'])
304 if dom < 0:
305 raise VmError('restore failed')
306 try:
307 vmconfig = sxp.from_string(d['vmconfig'])
308 config = sxp.child_value(vmconfig, 'config')
309 except Exception, ex:
310 raise VmError('config error: ' + str(ex))
311 deferred = vm.dom_construct(dom, config)
312 def vifs_cb(val, vm):
313 vif_up(vm.ipaddrs)
314 return vm
315 deferred.addCallback(vifs_cb, vm)
316 return deferred
318 def dom_get(dom):
319 """Get info from xen for an existing domain.
321 @param dom: domain id
322 @return: info or None
323 """
324 domlist = xc.domain_getinfo(dom, 1)
325 if domlist and dom == domlist[0]['dom']:
326 return domlist[0]
327 return None
329 def append_deferred(dlist, v):
330 """Append a value to a deferred list if it is a deferred.
332 @param dlist: list of deferreds
333 @param v: value to add
334 """
335 if isinstance(v, defer.Deferred):
336 dlist.append(v)
338 class XendDomainInfo:
339 """Virtual machine object."""
341 """Minimum time between domain restarts in seconds.
342 """
343 MINIMUM_RESTART_TIME = 20
345 def __init__(self):
346 self.recreate = 0
347 self.restore = 0
348 self.config = None
349 self.id = None
350 self.dom = None
351 self.cpu_weight = 1
352 self.start_time = None
353 self.name = None
354 self.memory = None
355 self.image = None
356 self.ramdisk = None
357 self.cmdline = None
358 self.console = None
359 self.devices = {}
360 self.device_index = {}
361 self.configs = []
362 self.info = None
363 self.ipaddrs = []
364 self.blkif_backend = 0
365 self.netif_backend = 0
366 #todo: state: running, suspended
367 self.state = STATE_VM_OK
368 #todo: set to migrate info if migrating
369 self.migrate = None
370 self.restart_mode = RESTART_ONREBOOT
371 self.restart_state = None
372 self.restart_time = None
373 self.console_port = None
375 def setdom(self, dom):
376 """Set the domain id.
378 @param dom: domain id
379 """
380 self.dom = int(dom)
381 self.id = str(dom)
383 def update(self, info):
384 """Update with info from xc.domain_getinfo().
385 """
386 self.info = info
387 self.memory = self.info['mem_kb'] / 1024
389 def __str__(self):
390 s = "domain"
391 s += " id=" + self.id
392 s += " name=" + self.name
393 s += " memory=" + str(self.memory)
394 if self.console:
395 s += " console=" + str(self.console.console_port)
396 if self.image:
397 s += " image=" + self.image
398 s += ""
399 return s
401 __repr__ = __str__
403 def sxpr(self):
404 sxpr = ['domain',
405 ['id', self.id],
406 ['name', self.name],
407 ['memory', self.memory] ]
409 if self.info:
410 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
411 run = (self.info['running'] and 'r') or '-'
412 block = (self.info['blocked'] and 'b') or '-'
413 pause = (self.info['paused'] and 'p') or '-'
414 shut = (self.info['shutdown'] and 's') or '-'
415 crash = (self.info['crashed'] and 'c') or '-'
416 state = run + block + pause + shut + crash
417 sxpr.append(['state', state])
418 if self.info['shutdown']:
419 reason = shutdown_reason(self.info['shutdown_reason'])
420 sxpr.append(['shutdown_reason', reason])
421 sxpr.append(['cpu', self.info['cpu']])
422 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
424 if self.start_time:
425 up_time = time.time() - self.start_time
426 sxpr.append(['up_time', str(up_time) ])
427 sxpr.append(['start_time', str(self.start_time) ])
429 if self.console:
430 sxpr.append(self.console.sxpr())
431 if self.restart_state:
432 sxpr.append(['restart_state', self.restart_state])
433 if self.restart_time:
434 sxpr.append(['restart_time', str(self.restart_time)])
435 if self.config:
436 sxpr.append(['config', self.config])
437 return sxpr
439 def check_name(self, name):
440 """Check if a vm name is valid. Valid names start with a non-digit
441 and contain alphabetic characters, digits, or characters in '_-.'.
442 The same name cannot be used for more than one vm at the same time.
444 @param name: name
445 @raise: VMerror if invalid
446 """
447 if self.recreate: return
448 if name is None or name == '':
449 raise VmError('missing vm name')
450 if name[0] in string.digits:
451 raise VmError('invalid vm name')
452 for c in name:
453 if c in string.digits: continue
454 if c in '_-.': continue
455 if c in string.ascii_letters: continue
456 raise VmError('invalid vm name')
457 dominfo = domain_exists(name)
458 # When creating or rebooting, a domain with my name should not exist.
459 # When restoring, a domain with my name will exist, but it should have
460 # my domain id.
461 if not dominfo:
462 return
463 print 'check_name>', 'dom=', dominfo.name, dominfo.dom, 'self=', name, self.dom
464 if dominfo.is_terminated():
465 return
466 if not self.dom or (dominfo.dom != self.dom):
467 raise VmError('vm name clash: ' + name)
469 def construct(self, config):
470 """Construct the vm instance from its configuration.
472 @param config: configuration
473 @return: deferred
474 @raise: VmError on error
475 """
476 # todo - add support for scheduling params?
477 self.config = config
478 try:
479 self.name = sxp.child_value(config, 'name')
480 self.check_name(self.name)
481 try:
482 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
483 except:
484 raise VmError('invalid cpu weight')
485 if self.restore and self.dom:
486 xc.domain_setname(self.dom, self.name)
487 self.memory = int(sxp.child_value(config, 'memory'))
488 if self.memory is None:
489 raise VmError('missing memory size')
491 self.configure_console()
492 self.configure_restart()
493 self.configure_backends()
494 image = sxp.child_value(config, 'image')
495 if image is None:
496 raise VmError('missing image')
497 image_name = sxp.name(image)
498 if image_name is None:
499 raise VmError('missing image name')
500 image_handler = get_image_handler(image_name)
501 if image_handler is None:
502 raise VmError('unknown image type: ' + image_name)
503 image_handler(self, image)
504 deferred = self.configure()
505 def cberr(err):
506 self.destroy()
507 return err
508 deferred.addErrback(cberr)
509 except StandardError, ex:
510 # Catch errors, cleanup and re-raise.
511 self.destroy()
512 raise
513 return deferred
515 def config_devices(self, name):
516 """Get a list of the 'device' nodes of a given type from the config.
518 @param name: device type
519 @type name: string
520 @return: device configs
521 @rtype: list
522 """
523 devices = []
524 for d in sxp.children(self.config, 'device'):
525 dev = sxp.child0(d)
526 if dev is None: continue
527 if name == sxp.name(dev):
528 devices.append(dev)
529 return devices
531 def config_device(self, type, idx):
532 """Get a device config from the device nodes of a given type
533 from the config.
535 @param type: device type
536 @type type: string
537 @param idx: index
538 @type idx: int
539 @return config or None
540 """
541 devs = self.config_devices(type)
542 if 0 <= idx < len(devs):
543 return devs[idx]
544 else:
545 return None
547 def next_device_index(self, type):
548 """Get the next index for a given device type.
550 @param type: device type
551 @type type: string
552 @return device index
553 @rtype: int
554 """
555 idx = self.device_index.get(type, 0)
556 self.device_index[type] = idx + 1
557 return idx
559 def add_device(self, type, dev):
560 """Add a device to a virtual machine.
562 @param type: device type
563 @param dev: device to add
564 """
565 dl = self.devices.get(type, [])
566 dl.append(dev)
567 self.devices[type] = dl
569 def get_devices(self, type):
570 """Get a list of the devices of a given type.
572 @param type: device type
573 @return: devices
574 """
575 val = self.devices.get(type, [])
576 return val
578 def get_device_by_id(self, type, id):
579 """Get the device with the given id.
581 @param id: device id
582 @return: device or None
583 """
584 dl = self.get_devices(type)
585 for d in dl:
586 if d.getprop('id') == id:
587 return d
588 return None
590 def get_device_by_index(self, type, idx):
591 """Get the device with the given index.
593 @param idx: device index
594 @return: device or None
595 """
596 idx = str(idx)
597 dl = self.get_devices(type)
598 for d in dl:
599 if d.getidx() == idx:
600 return d
601 return None
603 def add_config(self, val):
604 """Add configuration data to a virtual machine.
606 @param val: data to add
607 """
608 self.configs.append(val)
610 def destroy(self):
611 """Completely destroy the vm.
612 """
613 self.cleanup()
614 return self.destroy_domain()
616 def destroy_domain(self):
617 """Destroy the vm's domain.
618 The domain will not finally go away unless all vm
619 devices have been released.
620 """
621 if self.dom is None: return 0
622 if self.console:
623 if self.restart_pending():
624 self.console.deregisterChannel()
625 else:
626 log.debug('Closing console, domain %s', self.id)
627 self.console.close()
628 chan = xend.getDomChannel(self.dom)
629 if chan:
630 log.debug("Closing channel to domain %d", self.dom)
631 chan.close()
632 try:
633 return xc.domain_destroy(dom=self.dom)
634 except Exception, err:
635 log.exception("Domain destroy failed: %s", self.name)
637 def cleanup(self):
638 """Cleanup vm resources: release devices.
639 """
640 self.state = STATE_VM_TERMINATED
641 self.release_devices()
643 def is_terminated(self):
644 """Check if a domain has been terminated.
645 """
646 return self.state == STATE_VM_TERMINATED
648 def release_devices(self):
649 """Release all vm devices.
650 """
651 self.release_vifs()
652 self.release_vbds()
654 self.devices = {}
655 self.device_index = {}
656 self.configs = []
657 self.ipaddrs = []
659 def release_vifs(self):
660 """Release vm virtual network devices (vifs).
661 """
662 if self.dom is None: return
663 ctrl = xend.netif_get(self.dom)
664 if ctrl:
665 log.debug("Destroying vifs for domain %d", self.dom)
666 ctrl.destroy()
668 def release_vbds(self):
669 """Release vm virtual block devices (vbds).
670 """
671 if self.dom is None: return
672 ctrl = xend.blkif_get(self.dom)
673 if ctrl:
674 log.debug("Destroying vbds for domain %d", self.dom)
675 ctrl.destroy()
677 def show(self):
678 """Print virtual machine info.
679 """
680 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
681 print "image:"
682 sxp.show(self.image)
683 print
684 for dl in self.devices:
685 for dev in dl:
686 print "device:"
687 sxp.show(dev)
688 print
689 for val in self.configs:
690 print "config:"
691 sxp.show(val)
692 print
693 print "]"
695 def init_domain(self):
696 """Initialize the domain memory.
697 """
698 if self.recreate:
699 return
700 if self.start_time is None:
701 self.start_time = time.time()
702 if self.restore:
703 return
704 memory = self.memory
705 name = self.name
706 try:
707 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
708 except:
709 raise VmError('invalid cpu')
710 cpu_weight = self.cpu_weight
711 dom = self.dom or 0
712 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024,
713 name= name, cpu= cpu, cpu_weight= cpu_weight)
714 if dom <= 0:
715 raise VmError('Creating domain failed: name=%s memory=%d'
716 % (name, memory))
717 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
718 self.setdom(dom)
720 def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
721 """Build the domain boot image.
722 """
723 if self.recreate or self.restore: return
724 if len(cmdline) >= 256:
725 log.warning('kernel cmdline too long, domain %d', self.dom)
726 dom = self.dom
727 buildfn = getattr(xc, '%s_build' % ostype)
728 flags = 0
729 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
730 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
731 err = buildfn(dom = dom,
732 image = kernel,
733 control_evtchn = self.console.getRemotePort(),
734 cmdline = cmdline,
735 ramdisk = ramdisk,
736 flags = flags)
737 if err != 0:
738 raise VmError('Building domain failed: type=%s dom=%d err=%d'
739 % (ostype, dom, err))
741 def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
742 """Create a domain. Builds the image but does not configure it.
744 @param ostype: OS type
745 @param kernel: kernel image
746 @param ramdisk: kernel ramdisk
747 @param cmdline: kernel commandline
748 @param vifs_n: number of network interfaces
749 """
750 if not self.recreate:
751 if not os.path.isfile(kernel):
752 raise VmError('Kernel image does not exist: %s' % kernel)
753 if ramdisk and not os.path.isfile(ramdisk):
754 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
755 self.init_domain()
756 if self.console:
757 self.console.registerChannel()
758 else:
759 self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
760 self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
761 self.image = kernel
762 self.ramdisk = ramdisk
763 self.cmdline = cmdline
765 def create_devices(self):
766 """Create the devices for a vm.
768 @return: Deferred
769 @raise: VmError for invalid devices
770 """
771 dlist = []
772 devices = sxp.children(self.config, 'device')
773 index = {}
774 for d in devices:
775 dev = sxp.child0(d)
776 if dev is None:
777 raise VmError('invalid device')
778 dev_name = sxp.name(dev)
779 dev_index = index.get(dev_name, 0)
780 dev_handler = get_device_handler(dev_name)
781 if dev_handler is None:
782 raise VmError('unknown device type: ' + dev_name)
783 v = dev_handler(self, dev, dev_index)
784 append_deferred(dlist, v)
785 index[dev_name] = dev_index + 1
786 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
787 return deferred
789 def device_create(self, dev_config):
790 """Create a new device.
792 @param dev_config: device configuration
793 @return: deferred
794 """
795 dev_name = sxp.name(dev_config)
796 dev_handler = get_device_handler(dev_name)
797 if dev_handler is None:
798 raise VmError('unknown device type: ' + dev_name)
799 devs = self.get_devices(dev_name)
800 dev_index = len(devs)
801 self.config.append(['device', dev_config])
802 d = dev_handler(self, dev_config, dev_index)
803 return d
805 def device_destroy(self, type, idx):
806 """Destroy a device.
808 @param type: device type
809 @param idx: device index
810 """
811 dev = self.get_device_by_index(type, idx)
812 if not dev:
813 raise VmError('invalid device: %s %s' % (type, idx))
814 devs = self.devices.get(type)
815 index = devs.index(dev)
816 dev_config = self.config_device(type, index)
817 if dev_config:
818 self.config.remove(['device', dev_config])
819 dev.destroy()
821 def configure_console(self):
822 """Configure the vm console port.
823 """
824 x = sxp.child_value(self.config, 'console')
825 if x:
826 try:
827 port = int(x)
828 except:
829 raise VmError('invalid console:' + str(x))
830 self.console_port = port
832 def configure_restart(self):
833 """Configure the vm restart mode.
834 """
835 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
836 if r not in restart_modes:
837 raise VmError('invalid restart mode: ' + str(r))
838 self.restart_mode = r;
840 def restart_needed(self, reason):
841 """Determine if the vm needs to be restarted when shutdown
842 for the given reason.
844 @param reason: shutdown reason
845 @return 1 if needs restaert, 0 otherwise
846 """
847 if self.restart_mode == RESTART_NEVER:
848 return 0
849 if self.restart_mode == RESTART_ALWAYS:
850 return 1
851 if self.restart_mode == RESTART_ONREBOOT:
852 return reason == 'reboot'
853 return 0
855 def restart_cancel(self):
856 """Cancel a vm restart.
857 """
858 self.restart_state = None
860 def restarting(self):
861 """Put the vm into restart mode.
862 """
863 self.restart_state = STATE_RESTART_PENDING
865 def restart_pending(self):
866 """Test if the vm has a pending restart.
867 """
868 return self.restart_state == STATE_RESTART_PENDING
870 def restart_check(self):
871 """Check if domain restart is OK.
872 To prevent restart loops, raise an error if it is
873 less than MINIMUM_RESTART_TIME seconds since the last restart.
874 """
875 tnow = time.time()
876 if self.restart_time is not None:
877 tdelta = tnow - self.restart_time
878 if tdelta < self.MINIMUM_RESTART_TIME:
879 self.restart_cancel()
880 msg = 'VM %s restarting too fast' % self.name
881 log.error(msg)
882 raise VmError(msg)
883 self.restart_time = tnow
885 def restart(self):
886 """Restart the domain after it has exited.
887 Reuses the domain id and console port.
889 @return: deferred
890 """
891 try:
892 self.restart_check()
893 self.restart_state = STATE_RESTART_BOOTING
894 d = self.construct(self.config)
895 finally:
896 self.restart_state = None
897 return d
899 def configure_backends(self):
900 """Set configuration flags if the vm is a backend for netif or blkif.
901 Configure the backends to use for vbd and vif if specified.
902 """
903 for c in sxp.children(self.config, 'backend'):
904 v = sxp.child0(c)
905 name = sxp.name(v)
906 if name == 'blkif':
907 self.blkif_backend = 1
908 elif name == 'netif':
909 self.netif_backend = 1
910 else:
911 raise VmError('invalid backend type:' + str(name))
913 def configure(self):
914 """Configure a vm.
916 vm virtual machine
917 config configuration
919 returns Deferred - calls callback with vm
920 """
921 d = self.create_devices()
922 d.addCallback(self._configure)
923 return d
925 def _configure(self, val):
926 d = self.configure_fields()
927 def cbok(results):
928 return self
929 def cberr(err):
930 self.destroy()
931 return err
932 d.addCallback(cbok)
933 d.addErrback(cberr)
934 return d
936 def dom_construct(self, dom, config):
937 """Construct a vm for an existing domain.
939 @param dom: domain id
940 @return: deferred
941 """
942 d = dom_get(dom)
943 if not d:
944 raise VmError("Domain not found: %d" % dom)
945 print 'dom_construct>', dom, config
946 try:
947 self.restore = 1
948 self.setdom(dom)
949 self.name = d['name']
950 self.memory = d['mem_kb']/1024
951 deferred = self.construct(config)
952 finally:
953 self.restore = 0
954 return deferred
956 def configure_fields(self):
957 """Process the vm configuration fields using the registered handlers.
958 """
959 dlist = []
960 index = {}
961 for field in sxp.children(self.config):
962 field_name = sxp.name(field)
963 field_index = index.get(field_name, 0)
964 field_handler = get_config_handler(field_name)
965 # Ignore unknown fields. Warn?
966 if field_handler:
967 v = field_handler(self, self.config, field, field_index)
968 append_deferred(dlist, v)
969 else:
970 log.warning("Unknown config field %s", field_name)
971 index[field_name] = field_index + 1
972 d = defer.DeferredList(dlist, fireOnOneErrback=1)
973 return d
976 def vm_image_linux(vm, image):
977 """Create a VM for a linux image.
979 @param name: vm name
980 @param memory: vm memory
981 @param image: image config
982 @return: vm
983 """
984 kernel = sxp.child_value(image, "kernel")
985 cmdline = ""
986 ip = sxp.child_value(image, "ip", "dhcp")
987 if ip:
988 cmdline += " ip=" + ip
989 root = sxp.child_value(image, "root")
990 if root:
991 cmdline += " root=" + root
992 args = sxp.child_value(image, "args")
993 if args:
994 cmdline += " " + args
995 ramdisk = sxp.child_value(image, "ramdisk", '')
996 vifs = vm.config_devices("vif")
997 vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs))
998 return vm
1000 def vm_image_netbsd(vm, image):
1001 """Create a VM for a bsd image.
1003 @param name: vm name
1004 @param memory: vm memory
1005 @param image: image config
1006 @return: vm
1007 """
1008 #todo: Same as for linux. Is that right? If so can unify them.
1009 kernel = sxp.child_value(image, "kernel")
1010 cmdline = ""
1011 ip = sxp.child_value(image, "ip", "dhcp")
1012 if ip:
1013 cmdline += "ip=" + ip
1014 root = sxp.child_value(image, "root")
1015 if root:
1016 cmdline += "root=" + root
1017 args = sxp.child_value(image, "args")
1018 if args:
1019 cmdline += " " + args
1020 ramdisk = sxp.child_value(image, "ramdisk", '')
1021 vifs = vm.config_devices("vif")
1022 vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs))
1023 return vm
1026 def vm_dev_vif(vm, val, index):
1027 """Create a virtual network interface (vif).
1029 @param vm: virtual machine
1030 @param val: vif config
1031 @param index: vif index
1032 @return: deferred
1033 """
1034 #if vm.netif_backend:
1035 # raise VmError('vif: vif in netif backend domain')
1036 vif = vm.next_device_index('vif')
1037 vmac = sxp.child_value(val, "mac")
1038 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1039 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1040 defer = ctrl.attachDevice(vif, val, recreate=vm.recreate)
1041 def cbok(dev):
1042 dev.vifctl('up', vmname=vm.name)
1043 vm.add_device('vif', dev)
1044 return dev
1045 defer.addCallback(cbok)
1046 return defer
1048 def vm_dev_vbd(vm, val, index):
1049 """Create a virtual block device (vbd).
1051 @param vm: virtual machine
1052 @param val: vbd config
1053 @param index: vbd index
1054 @return: deferred
1055 """
1056 #if vm.blkif_backend:
1057 # raise VmError('vbd: vbd in blkif backend domain')
1058 uname = sxp.child_value(val, 'uname')
1059 if not uname:
1060 raise VmError('vbd: Missing uname')
1061 dev = sxp.child_value(val, 'dev')
1062 if not dev:
1063 raise VmError('vbd: Missing dev')
1064 mode = sxp.child_value(val, 'mode', 'r')
1065 log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
1066 defer = make_disk(vm, val, uname, dev, mode, vm.recreate)
1067 def fn(vbd):
1068 vbd.dev = dev
1069 vbd.uname = uname
1070 vm.add_device('vbd', vbd)
1071 return vbd
1072 defer.addCallback(fn)
1073 return defer
1075 def parse_pci(val):
1076 """Parse a pci field.
1077 """
1078 if isinstance(val, types.StringType):
1079 radix = 10
1080 if val.startswith('0x') or val.startswith('0X'):
1081 radix = 16
1082 v = int(val, radix)
1083 else:
1084 v = val
1085 return v
1087 def vm_dev_pci(vm, val, index):
1088 """Add a pci device.
1090 @param vm: virtual machine
1091 @param val: device configuration
1092 @param index: device index
1093 @return: 0 on success
1094 """
1095 bus = sxp.child_value(val, 'bus')
1096 if not bus:
1097 raise VmError('pci: Missing bus')
1098 dev = sxp.child_value(val, 'dev')
1099 if not dev:
1100 raise VmError('pci: Missing dev')
1101 func = sxp.child_value(val, 'func')
1102 if not func:
1103 raise VmError('pci: Missing func')
1104 try:
1105 bus = parse_pci(bus)
1106 dev = parse_pci(dev)
1107 func = parse_pci(func)
1108 except:
1109 raise VmError('pci: invalid parameter')
1110 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1111 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1112 func=func, enable=1)
1113 if rc < 0:
1114 #todo non-fatal
1115 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1116 (bus, dev, func))
1117 return rc
1120 def vm_field_ignore(vm, config, val, index):
1121 """Dummy config field handler used for fields with built-in handling.
1123 @param vm: virtual machine
1124 @param config: vm config
1125 @param val: vfr field
1126 @param index: field index
1127 """
1128 pass
1130 # Register image handlers.
1131 add_image_handler('linux', vm_image_linux)
1132 add_image_handler('netbsd', vm_image_netbsd)
1134 # Register device handlers.
1135 add_device_handler('vif', vm_dev_vif)
1136 add_device_handler('vbd', vm_dev_vbd)
1137 add_device_handler('pci', vm_dev_pci)
1139 # Ignore the fields we already handle.
1140 add_config_handler('name', vm_field_ignore)
1141 add_config_handler('memory', vm_field_ignore)
1142 add_config_handler('cpu', vm_field_ignore)
1143 add_config_handler('cpu_weight', vm_field_ignore)
1144 add_config_handler('console', vm_field_ignore)
1145 add_config_handler('image', vm_field_ignore)
1146 add_config_handler('device', vm_field_ignore)
1147 add_config_handler('backend', vm_field_ignore)
1149 # Register other config handlers.