ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 2154:e5f6ec915c89

bitkeeper revision 1.1159.13.1 (4118a004V4kwgoMR9dGS7kTWxK4rmg)

Fix restart problem caused by old info in domain object.
author mjw@wray-m-3.hpl.hp.com
date Tue Aug 10 10:14:28 2004 +0000 (2004-08-10)
parents 99bda1b6ff7f
children 3ba188e96131
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"
72 def shutdown_reason(code):
73 """Get a shutdown reason from a code.
75 @param code: shutdown code
76 @type code: int
77 @return: shutdown reason
78 @rtype: string
79 """
80 return shutdown_reasons.get(code, "?")
82 def blkdev_name_to_number(name):
83 """Take the given textual block-device name (e.g., '/dev/sda1',
84 'hda') and return the device number used by the OS. """
86 if not re.match( '^/dev/', name ):
87 n = '/dev/' + name
88 else:
89 n = name
91 try:
92 return os.stat(n).st_rdev
93 except:
94 pass
96 # see if this is a hex device number
97 if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
98 return string.atoi(name,16)
100 return None
102 def lookup_raw_partn(name):
103 """Take the given block-device name (e.g., '/dev/sda1', 'hda')
104 and return a dictionary { device, start_sector,
105 nr_sectors, type }
106 device: Device number of the given partition
107 start_sector: Index of first sector of the partition
108 nr_sectors: Number of sectors comprising this partition
109 type: 'Disk' or identifying name for partition type
110 """
112 n = blkdev_name_to_number(name)
113 if n:
114 return [ { 'device' : n,
115 'start_sector' : long(0),
116 'nr_sectors' : long(1L<<63),
117 'type' : 'Disk' } ]
118 else:
119 return None
121 def lookup_disk_uname(uname):
122 """Lookup a list of segments for a physical device.
123 uname [string]: name of the device in the format \'phy:dev\' for a physical device
124 returns [list of dicts]: list of extents that make up the named device
125 """
126 ( type, d_name ) = string.split( uname, ':' )
128 if type == "phy":
129 segments = lookup_raw_partn( d_name )
130 else:
131 segments = None
132 return segments
134 def make_disk(dom, uname, dev, mode, recreate=0):
135 """Create a virtual disk device for a domain.
137 @param dom: domain id
138 @param uname: device to export
139 @param dev: device name in domain
140 @param mode: read/write mode
141 @param recreate: recreate flag (after xend restart)
142 @return: deferred
143 """
144 segments = lookup_disk_uname(uname)
145 if not segments:
146 raise VmError("vbd: Segments not found: uname=%s" % uname)
147 if len(segments) > 1:
148 raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
149 segment = segments[0]
150 vdev = blkdev_name_to_number(dev)
151 ctrl = xend.blkif_create(dom, recreate=recreate)
153 def fn(ctrl):
154 return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate)
155 ctrl.addCallback(fn)
156 return ctrl
158 def vif_up(iplist):
159 """send an unsolicited ARP reply for all non link-local IP addresses.
161 iplist IP addresses
162 """
164 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
166 def get_ip_nonlocal_bind():
167 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
169 def set_ip_nonlocal_bind(v):
170 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
172 def link_local(ip):
173 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
175 def arping(ip, gw):
176 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
177 print cmd
178 os.system(cmd)
180 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
181 nlb = get_ip_nonlocal_bind()
182 if not nlb: set_ip_nonlocal_bind(1)
183 try:
184 for ip in iplist:
185 if not link_local(ip):
186 arping(ip, gateway)
187 finally:
188 if not nlb: set_ip_nonlocal_bind(0)
190 config_handlers = {}
192 def add_config_handler(name, h):
193 """Add a handler for a config field.
195 @param name: field name
196 @param h: handler: fn(vm, config, field, index)
197 """
198 config_handlers[name] = h
200 def get_config_handler(name):
201 """Get a handler for a config field.
203 returns handler or None
204 """
205 return config_handlers.get(name)
207 """Table of handlers for virtual machine images.
208 Indexed by image type.
209 """
210 image_handlers = {}
212 def add_image_handler(name, h):
213 """Add a handler for an image type
214 @param name: image type
215 @param h: handler: fn(config, name, memory, image)
216 """
217 image_handlers[name] = h
219 def get_image_handler(name):
220 """Get the handler for an image type.
221 @param name: image type
222 @return: handler or None
223 """
224 return image_handlers.get(name)
226 """Table of handlers for devices.
227 Indexed by device type.
228 """
229 device_handlers = {}
231 def add_device_handler(name, h):
232 """Add a handler for a device type.
234 @param name: device type
235 @param h: handler: fn(vm, dev)
236 """
237 device_handlers[name] = h
239 def get_device_handler(name):
240 """Get the handler for a device type.
242 @param name : device type
243 @return; handler or None
244 """
245 return device_handlers.get(name)
247 def vm_create(config):
248 """Create a VM from a configuration.
249 If a vm has been partially created and there is an error it
250 is destroyed.
252 @param config configuration
253 @return: Deferred
254 @raise: VmError for invalid configuration
255 """
256 vm = XendDomainInfo()
257 return vm.construct(config)
259 def vm_recreate(savedinfo, info):
260 """Create the VM object for an existing domain.
262 @param savedinfo: saved info from the domain DB
263 @type savedinfo: sxpr
264 @param info: domain info from xc
265 @type info: xc domain dict
266 @return: deferred
267 """
268 vm = XendDomainInfo()
269 vm.recreate = 1
270 vm.setdom(info['dom'])
271 vm.name = info['name']
272 vm.memory = info['mem_kb']/1024
273 start_time = sxp.child_value(savedinfo, 'start_time')
274 if start_time is not None:
275 vm.start_time = float(start_time)
276 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
277 restart_time = sxp.child_value(savedinfo, 'restart_time')
278 if restart_time is not None:
279 vm.restart_time = float(restart_time)
280 config = sxp.child_value(savedinfo, 'config')
281 if config:
282 d = vm.construct(config)
283 else:
284 d = defer.Deferred()
285 d.callback(vm)
286 vm.recreate = 0
287 return d
289 def vm_restore(src, progress=0):
290 """Restore a VM from a disk image.
292 src saved state to restore
293 progress progress reporting flag
294 returns deferred
295 raises VmError for invalid configuration
296 """
297 vm = XendDomainInfo()
298 ostype = "linux" #todo Set from somewhere (store in the src?).
299 restorefn = getattr(xc, "%s_restore" % ostype)
300 d = restorefn(state_file=src, progress=progress)
301 dom = int(d['dom'])
302 if dom < 0:
303 raise VmError('restore failed')
304 vmconfig = sxp.from_string(d['vmconfig'])
305 vm.config = sxp.child_value(vmconfig, 'config')
306 deferred = vm.dom_configure(dom)
307 def vifs_cb(val, vm):
308 vif_up(vm.ipaddrs)
309 deferred.addCallback(vifs_cb, vm)
310 return deferred
312 def dom_get(dom):
313 """Get info from xen for an existing domain.
315 @param dom: domain id
316 @return: info or None
317 """
318 domlist = xc.domain_getinfo(dom=dom)
319 if domlist and dom == domlist[0]['dom']:
320 return domlist[0]
321 return None
323 def append_deferred(dlist, v):
324 """Append a value to a deferred list if it is a deferred.
326 @param dlist: list of deferreds
327 @param v: value to add
328 """
329 if isinstance(v, defer.Deferred):
330 dlist.append(v)
332 def _vm_configure1(val, vm):
333 d = vm.create_devices()
334 d.addCallback(_vm_configure2, vm)
335 return d
337 def _vm_configure2(val, vm):
338 d = vm.configure_fields()
339 def cbok(results):
340 return vm
341 def cberr(err):
342 vm.destroy()
343 return err
344 d.addCallback(cbok)
345 d.addErrback(cberr)
346 return d
348 class XendDomainInfo:
349 """Virtual machine object."""
351 """Minimum time between domain restarts in seconds.
352 """
353 MINIMUM_RESTART_TIME = 20
355 def __init__(self):
356 self.recreate = 0
357 self.config = None
358 self.id = None
359 self.dom = None
360 self.start_time = None
361 self.name = None
362 self.memory = None
363 self.image = None
364 self.ramdisk = None
365 self.cmdline = None
366 self.console = None
367 self.devices = {}
368 self.device_index = {}
369 self.configs = []
370 self.info = None
371 self.ipaddrs = []
372 self.blkif_backend = 0
373 self.netif_backend = 0
374 #todo: state: running, suspended
375 self.state = STATE_VM_OK
376 #todo: set to migrate info if migrating
377 self.migrate = None
378 self.restart_mode = RESTART_ONREBOOT
379 self.restart_state = None
380 self.restart_time = None
381 self.console_port = None
383 def setdom(self, dom):
384 """Set the domain id.
386 @param dom: domain id
387 """
388 self.dom = int(dom)
389 self.id = str(dom)
391 def update(self, info):
392 """Update with info from xc.domain_getinfo().
393 """
394 self.info = info
395 self.memory = self.info['mem_kb'] / 1024
397 def __str__(self):
398 s = "domain"
399 s += " id=" + self.id
400 s += " name=" + self.name
401 s += " memory=" + str(self.memory)
402 if self.console:
403 s += " console=" + str(self.console.console_port)
404 if self.image:
405 s += " image=" + self.image
406 s += ""
407 return s
409 __repr__ = __str__
411 def sxpr(self):
412 sxpr = ['domain',
413 ['id', self.id],
414 ['name', self.name],
415 ['memory', self.memory] ]
417 if self.info:
418 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
419 run = (self.info['running'] and 'r') or '-'
420 block = (self.info['blocked'] and 'b') or '-'
421 pause = (self.info['paused'] and 'p') or '-'
422 shut = (self.info['shutdown'] and 's') or '-'
423 crash = (self.info['crashed'] and 'c') or '-'
424 state = run + block + pause + shut + crash
425 sxpr.append(['state', state])
426 if self.info['shutdown']:
427 reason = shutdown_reason(self.info['shutdown_reason'])
428 sxpr.append(['shutdown_reason', reason])
429 sxpr.append(['cpu', self.info['cpu']])
430 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
432 if self.start_time:
433 up_time = time.time() - self.start_time
434 sxpr.append(['up_time', str(up_time) ])
435 sxpr.append(['start_time', str(self.start_time) ])
437 if self.console:
438 sxpr.append(self.console.sxpr())
439 if self.restart_state:
440 sxpr.append(['restart_state', self.restart_state])
441 if self.restart_time:
442 sxpr.append(['restart_time', str(self.restart_time)])
443 if self.config:
444 sxpr.append(['config', self.config])
445 return sxpr
447 def check_name(self, name):
448 """Check if a vm name is valid. Valid names start with a non-digit
449 and contain alphabetic characters, digits, or characters in '_-.'.
450 The same name cannot be used for more than one vm at the same time.
452 @param name: name
453 @raise: VMerror if invalid
454 """
455 if name is None or name == '':
456 raise VmError('missing vm name')
457 if name[0] in string.digits:
458 raise VmError('invalid vm name')
459 for c in name:
460 if c in string.digits: continue
461 if c in '_-.': continue
462 if c in string.ascii_letters: continue
463 raise VmError('invalid vm name')
464 # See comment in XendDomain constructor.
465 xd = get_component('xen.xend.XendDomain')
466 if xd.domain_exists(name):
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 self.memory = int(sxp.child_value(config, 'memory'))
482 if self.memory is None:
483 raise VmError('missing memory size')
485 self.configure_console()
486 self.configure_restart()
487 self.configure_backends()
488 image = sxp.child_value(config, 'image')
489 if image is None:
490 raise VmError('missing image')
491 image_name = sxp.name(image)
492 if image_name is None:
493 raise VmError('missing image name')
494 image_handler = get_image_handler(image_name)
495 if image_handler is None:
496 raise VmError('unknown image type: ' + image_name)
497 image_handler(self, image)
498 deferred = self.configure()
499 def cberr(err):
500 self.destroy()
501 return err
502 deferred.addErrback(cberr)
503 except StandardError, ex:
504 # Catch errors, cleanup and re-raise.
505 self.destroy()
506 raise
507 return deferred
509 def config_devices(self, name):
510 """Get a list of the 'device' nodes of a given type from the config.
512 @param name: device type
513 @type name: string
514 @return: device configs
515 @rtype: list
516 """
517 devices = []
518 for d in sxp.children(self.config, 'device'):
519 dev = sxp.child0(d)
520 if dev is None: continue
521 if name == sxp.name(dev):
522 devices.append(dev)
523 return devices
525 def config_device(self, type, idx):
526 """Get a device config from the device nodes of a given type
527 from the config.
529 @param type: device type
530 @type type: string
531 @param idx: index
532 @type idx: int
533 @return config or None
534 """
535 devs = self.config_devices(type)
536 if 0 <= idx < len(devs):
537 return devs[idx]
538 else:
539 return None
541 def next_device_index(self, type):
542 """Get the next index for a given device type.
544 @param type: device type
545 @type type: string
546 @return device index
547 @rtype: int
548 """
549 idx = self.device_index.get(type, 0)
550 self.device_index[type] = idx + 1
551 return idx
553 def add_device(self, type, dev):
554 """Add a device to a virtual machine.
556 @param type: device type
557 @param dev: device to add
558 """
559 dl = self.devices.get(type, [])
560 dl.append(dev)
561 self.devices[type] = dl
563 def get_devices(self, type):
564 """Get a list of the devices of a given type.
566 @param type: device type
567 @return: devices
568 """
569 val = self.devices.get(type, [])
570 return val
572 def get_device_by_id(self, type, id):
573 """Get the device with the given id.
575 @param id: device id
576 @return: device or None
577 """
578 dl = self.get_devices(type)
579 for d in dl:
580 if d.getprop('id') == id:
581 return d
582 return None
584 def get_device_by_index(self, type, idx):
585 """Get the device with the given index.
587 @param idx: device index
588 @return: device or None
589 """
590 idx = str(idx)
591 dl = self.get_devices(type)
592 for d in dl:
593 if d.getidx() == idx:
594 return d
595 return None
597 def add_config(self, val):
598 """Add configuration data to a virtual machine.
600 @param val: data to add
601 """
602 self.configs.append(val)
604 def destroy(self):
605 """Completely destroy the vm.
606 """
607 self.cleanup()
608 return self.destroy_domain()
610 def destroy_domain(self):
611 """Destroy the vm's domain.
612 The domain will not finally go away unless all vm
613 devices have been released.
614 """
615 if self.dom is None: return 0
616 if self.console:
617 if self.restart_pending():
618 self.console.deregisterChannel()
619 else:
620 self.console.close()
621 chan = xend.getDomChannel(self.dom)
622 if chan:
623 log.debug("Closing channel to domain %d", self.dom)
624 chan.close()
625 try:
626 return xc.domain_destroy(dom=self.dom)
627 except Exception, err:
628 log.exception("Domain destroy failed: ", self.name)
630 def cleanup(self):
631 """Cleanup vm resources: release devices.
632 """
633 self.state = STATE_VM_TERMINATED
634 self.release_devices()
636 def is_terminated(self):
637 """Check if a domain has been terminated.
638 """
639 return self.state == STATE_VM_TERMINATED
641 def release_devices(self):
642 """Release all vm devices.
643 """
644 self.release_vifs()
645 self.release_vbds()
647 self.devices = {}
648 self.device_index = {}
649 self.configs = []
650 self.ipaddrs = []
652 def release_vifs(self):
653 """Release vm virtual network devices (vifs).
654 """
655 if self.dom is None: return
656 ctrl = xend.netif_get(self.dom)
657 if ctrl:
658 log.debug("Destroying vifs for domain %d", self.dom)
659 ctrl.destroy()
661 def release_vbds(self):
662 """Release vm virtual block devices (vbds).
663 """
664 if self.dom is None: return
665 ctrl = xend.blkif_get(self.dom)
666 if ctrl:
667 log.debug("Destroying vbds for domain %d", self.dom)
668 ctrl.destroy()
670 def show(self):
671 """Print virtual machine info.
672 """
673 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
674 print "image:"
675 sxp.show(self.image)
676 print
677 for dl in self.devices:
678 for dev in dl:
679 print "device:"
680 sxp.show(dev)
681 print
682 for val in self.configs:
683 print "config:"
684 sxp.show(val)
685 print
686 print "]"
688 def init_domain(self):
689 """Initialize the domain memory.
690 """
691 if self.recreate: return
692 memory = self.memory
693 name = self.name
694 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
695 dom = self.dom or 0
696 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024, name= name, cpu= cpu)
697 if dom <= 0:
698 raise VmError('Creating domain failed: name=%s memory=%d'
699 % (name, memory))
700 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
701 self.setdom(dom)
703 if self.start_time is None:
704 self.start_time = time.time()
706 def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
707 """Build the domain boot image.
708 """
709 if self.recreate: return
710 if len(cmdline) >= 256:
711 log.warning('kernel cmdline too long, domain %d', self.dom)
712 dom = self.dom
713 buildfn = getattr(xc, '%s_build' % ostype)
714 flags = 0
715 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
716 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
717 err = buildfn(dom = dom,
718 image = kernel,
719 control_evtchn = self.console.getRemotePort(),
720 cmdline = cmdline,
721 ramdisk = ramdisk,
722 flags = flags)
723 if err != 0:
724 raise VmError('Building domain failed: type=%s dom=%d err=%d'
725 % (ostype, dom, err))
727 def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
728 """Create a domain. Builds the image but does not configure it.
730 @param ostype: OS type
731 @param kernel: kernel image
732 @param ramdisk: kernel ramdisk
733 @param cmdline: kernel commandline
734 @param vifs_n: number of network interfaces
735 """
736 if not self.recreate:
737 if not os.path.isfile(kernel):
738 raise VmError('Kernel image does not exist: %s' % kernel)
739 if ramdisk and not os.path.isfile(ramdisk):
740 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
741 self.init_domain()
742 if self.console:
743 self.console.registerChannel()
744 else:
745 self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
746 self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
747 self.image = kernel
748 self.ramdisk = ramdisk
749 self.cmdline = cmdline
751 def create_devices(self):
752 """Create the devices for a vm.
754 @return: Deferred
755 @raise: VmError for invalid devices
756 """
757 dlist = []
758 devices = sxp.children(self.config, 'device')
759 index = {}
760 for d in devices:
761 dev = sxp.child0(d)
762 if dev is None:
763 raise VmError('invalid device')
764 dev_name = sxp.name(dev)
765 dev_index = index.get(dev_name, 0)
766 dev_handler = get_device_handler(dev_name)
767 if dev_handler is None:
768 raise VmError('unknown device type: ' + dev_name)
769 v = dev_handler(self, dev, dev_index)
770 append_deferred(dlist, v)
771 index[dev_name] = dev_index + 1
772 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
773 return deferred
775 def device_create(self, dev_config):
776 """Create a new device.
778 @param dev_config: device configuration
779 @return: deferred
780 """
781 dev_name = sxp.name(dev_config)
782 dev_handler = get_device_handler(dev_name)
783 if dev_handler is None:
784 raise VmError('unknown device type: ' + dev_name)
785 devs = self.get_devices(dev_name)
786 dev_index = len(devs)
787 self.config.append(['device', dev_config])
788 d = dev_handler(self, dev_config, dev_index)
789 return d
791 def device_destroy(self, type, idx):
792 """Destroy a device.
794 @param type: device type
795 @param idx: device index
796 """
797 dev = self.get_device_by_index(type, idx)
798 if not dev:
799 raise VmError('invalid device: %s %s' % (type, idx))
800 devs = self.devices.get(type)
801 index = devs.index(dev)
802 dev_config = self.config_device(type, index)
803 if dev_config:
804 self.config.remove(['device', dev_config])
805 dev.destroy()
807 def configure_console(self):
808 """Configure the vm console port.
809 """
810 x = sxp.child_value(self.config, 'console')
811 if x:
812 try:
813 port = int(x)
814 except:
815 raise VmError('invalid console:' + str(x))
816 self.console_port = port
818 def configure_restart(self):
819 """Configure the vm restart mode.
820 """
821 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
822 if r not in restart_modes:
823 raise VmError('invalid restart mode: ' + str(r))
824 self.restart_mode = r;
826 def restart_needed(self, reason):
827 """Determine if the vm needs to be restarted when shutdown
828 for the given reason.
830 @param reason: shutdown reason
831 @return 1 if needs restaert, 0 otherwise
832 """
833 if self.restart_mode == RESTART_NEVER:
834 return 0
835 if self.restart_mode == RESTART_ALWAYS:
836 return 1
837 if self.restart_mode == RESTART_ONREBOOT:
838 return reason == 'reboot'
839 return 0
841 def restart_cancel(self):
842 """Cancel a vm restart.
843 """
844 self.restart_state = None
846 def restarting(self):
847 """Put the vm into restart mode.
848 """
849 self.restart_state = STATE_RESTART_PENDING
851 def restart_pending(self):
852 """Test if the vm has a pending restart.
853 """
854 return self.restart_state == STATE_RESTART_PENDING
856 def restart_check(self):
857 """Check if domain restart is OK.
858 To prevent restart loops, raise an error if it is
859 less than MINIMUM_RESTART_TIME seconds since the last restart.
860 """
861 tnow = time.time()
862 if self.restart_time is not None:
863 tdelta = tnow - self.restart_time
864 if tdelta < self.MINIMUM_RESTART_TIME:
865 self.restart_cancel()
866 msg = 'VM %s restarting too fast' % self.name
867 log.error(msg)
868 raise VmError(msg)
869 self.restart_time = tnow
871 def restart(self):
872 """Restart the domain after it has exited.
873 Reuses the domain id and console port.
875 @return: deferred
876 """
877 try:
878 self.restart_check()
879 self.restart_state = STATE_RESTART_BOOTING
880 d = self.construct(self.config)
881 finally:
882 self.restart_state = None
883 return d
885 def configure_backends(self):
886 """Set configuration flags if the vm is a backend for netif of blkif.
887 """
888 for c in sxp.children(self.config, 'backend'):
889 name = sxp.name(sxp.child0(c))
890 if name == 'blkif':
891 self.blkif_backend = 1
892 elif name == 'netif':
893 self.netif_backend = 1
894 else:
895 raise VmError('invalid backend type:' + str(name))
897 def create_backends(self):
898 """Setup the netif and blkif backends.
899 """
900 if self.blkif_backend:
901 xend.blkif_set_control_domain(self.dom, recreate=self.recreate)
902 if self.netif_backend:
903 xend.netif_set_control_domain(self.dom, recreate=self.recreate)
905 def configure(self):
906 """Configure a vm.
908 vm virtual machine
909 config configuration
911 returns Deferred - calls callback with vm
912 """
913 if self.blkif_backend:
914 d = defer.Deferred()
915 d.callback(self)
916 else:
917 d = xend.blkif_create(self.dom, recreate=self.recreate)
918 d.addCallback(_vm_configure1, self)
919 return d
921 def dom_configure(self, dom):
922 """Configure a vm for an existing domain.
924 @param dom: domain id
925 @return: deferred
926 """
927 d = dom_get(dom)
928 if not d:
929 raise VmError("Domain not found: %d" % dom)
930 try:
931 self.setdom(dom)
932 self.name = d['name']
933 self.memory = d['memory']/1024
934 deferred = self.configure()
935 def cberr(err):
936 self.destroy()
937 return err
938 deferred.addErrback(cberr)
939 except StandardError, ex:
940 self.destroy()
941 raise
942 return deferred
944 def configure_fields(self):
945 """Process the vm configuration fields using the registered handlers.
946 """
947 dlist = []
948 index = {}
949 for field in sxp.children(self.config):
950 field_name = sxp.name(field)
951 field_index = index.get(field_name, 0)
952 field_handler = get_config_handler(field_name)
953 # Ignore unknown fields. Warn?
954 if field_handler:
955 v = field_handler(self, self.config, field, field_index)
956 append_deferred(dlist, v)
957 else:
958 log.warning("Unknown config field %s", field_name)
959 index[field_name] = field_index + 1
960 d = defer.DeferredList(dlist, fireOnOneErrback=1)
961 return d
964 def vm_image_linux(vm, image):
965 """Create a VM for a linux image.
967 @param name: vm name
968 @param memory: vm memory
969 @param image: image config
970 @return: vm
971 """
972 kernel = sxp.child_value(image, "kernel")
973 cmdline = ""
974 ip = sxp.child_value(image, "ip", "dhcp")
975 if ip:
976 cmdline += " ip=" + ip
977 root = sxp.child_value(image, "root")
978 if root:
979 cmdline += " root=" + root
980 args = sxp.child_value(image, "args")
981 if args:
982 cmdline += " " + args
983 ramdisk = sxp.child_value(image, "ramdisk", '')
984 vifs = vm.config_devices("vif")
985 vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs))
986 return vm
988 def vm_image_netbsd(vm, image):
989 """Create a VM for a bsd image.
991 @param name: vm name
992 @param memory: vm memory
993 @param image: image config
994 @return: vm
995 """
996 #todo: Same as for linux. Is that right? If so can unify them.
997 kernel = sxp.child_value(image, "kernel")
998 cmdline = ""
999 ip = sxp.child_value(image, "ip", "dhcp")
1000 if ip:
1001 cmdline += "ip=" + ip
1002 root = sxp.child_value(image, "root")
1003 if root:
1004 cmdline += "root=" + root
1005 args = sxp.child_value(image, "args")
1006 if args:
1007 cmdline += " " + args
1008 ramdisk = sxp.child_value(image, "ramdisk", '')
1009 vifs = vm.config_devices("vif")
1010 vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs))
1011 return vm
1014 def vm_dev_vif(vm, val, index):
1015 """Create a virtual network interface (vif).
1017 @param vm: virtual machine
1018 @param val: vif config
1019 @param index: vif index
1020 @return: deferred
1021 """
1022 if vm.netif_backend:
1023 raise VmError('vif: vif in netif backend domain')
1024 vif = vm.next_device_index('vif')
1025 vmac = sxp.child_value(val, "mac")
1026 xend.netif_create(vm.dom, recreate=vm.recreate)
1027 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1028 defer = xend.netif_dev_create(vm.dom, vif, val, recreate=vm.recreate)
1029 def fn(id):
1030 dev = xend.netif_dev(vm.dom, vif)
1031 dev.vifctl('up', vmname=vm.name)
1032 vm.add_device('vif', dev)
1033 return id
1034 defer.addCallback(fn)
1035 return defer
1037 def vm_dev_vbd(vm, val, index):
1038 """Create a virtual block device (vbd).
1040 @param vm: virtual machine
1041 @param val: vbd config
1042 @param index: vbd index
1043 @return: deferred
1044 """
1045 if vm.blkif_backend:
1046 raise VmError('vbd: vbd in blkif backend domain')
1047 uname = sxp.child_value(val, 'uname')
1048 if not uname:
1049 raise VmError('vbd: Missing uname')
1050 dev = sxp.child_value(val, 'dev')
1051 if not dev:
1052 raise VmError('vbd: Missing dev')
1053 mode = sxp.child_value(val, 'mode', 'r')
1054 log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
1055 defer = make_disk(vm.dom, uname, dev, mode, vm.recreate)
1056 def fn(vbd):
1057 vbd.dev = dev
1058 vbd.uname = uname
1059 vm.add_device('vbd', vbd)
1060 return vbd
1061 defer.addCallback(fn)
1062 return defer
1064 def parse_pci(val):
1065 """Parse a pci field.
1066 """
1067 if isinstance(val, types.StringType):
1068 radix = 10
1069 if val.startswith('0x') or val.startswith('0X'):
1070 radix = 16
1071 v = int(val, radix)
1072 else:
1073 v = val
1074 return v
1076 def vm_dev_pci(vm, val, index):
1077 """Add a pci device.
1079 @param vm: virtual machine
1080 @param val: device configuration
1081 @param index: device index
1082 @return: 0 on success
1083 """
1084 bus = sxp.child_value(val, 'bus')
1085 if not bus:
1086 raise VmError('pci: Missing bus')
1087 dev = sxp.child_value(val, 'dev')
1088 if not dev:
1089 raise VmError('pci: Missing dev')
1090 func = sxp.child_value(val, 'func')
1091 if not func:
1092 raise VmError('pci: Missing func')
1093 try:
1094 bus = parse_pci(bus)
1095 dev = parse_pci(dev)
1096 func = parse_pci(func)
1097 except:
1098 raise VmError('pci: invalid parameter')
1099 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1100 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1101 func=func, enable=1)
1102 if rc < 0:
1103 #todo non-fatal
1104 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1105 (bus, dev, func))
1106 return rc
1109 def vm_field_ignore(vm, config, val, index):
1110 """Dummy config field handler used for fields with built-in handling.
1112 @param vm: virtual machine
1113 @param config: vm config
1114 @param val: vfr field
1115 @param index: field index
1116 """
1117 pass
1119 # Register image handlers.
1120 add_image_handler('linux', vm_image_linux)
1121 add_image_handler('netbsd', vm_image_netbsd)
1123 # Register device handlers.
1124 add_device_handler('vif', vm_dev_vif)
1125 add_device_handler('vbd', vm_dev_vbd)
1126 add_device_handler('pci', vm_dev_pci)
1128 # Ignore the fields we already handle.
1129 add_config_handler('name', vm_field_ignore)
1130 add_config_handler('memory', vm_field_ignore)
1131 add_config_handler('cpu', vm_field_ignore)
1132 add_config_handler('console', vm_field_ignore)
1133 add_config_handler('image', vm_field_ignore)
1134 add_config_handler('device', vm_field_ignore)
1135 add_config_handler('backend', vm_field_ignore)
1137 # Register other config handlers.