ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 2545:de88814afd44

bitkeeper revision 1.1159.1.175 (415445ceydjmiY9bcerArlIlTqqZvQ)

Put configuring the console port back where it belonged.
author mjw@wray-m-3.hpl.hp.com
date Fri Sep 24 16:05:34 2004 +0000 (2004-09-24)
parents 0f827b0e0801
children 82cab392896d 8bc05c989fb3
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 """The length of domain names that Xen can handle.
37 The names stored in Xen itself are not used for much, and
38 xend can handle domain names of any length.
39 """
40 MAX_DOMAIN_NAME = 15
42 """Flag for a block device backend domain."""
43 SIF_BLK_BE_DOMAIN = (1<<4)
45 """Flag for a net device backend domain."""
46 SIF_NET_BE_DOMAIN = (1<<5)
48 """Shutdown code for poweroff."""
49 DOMAIN_POWEROFF = 0
50 """Shutdown code for reboot."""
51 DOMAIN_REBOOT = 1
52 """Shutdown code for suspend."""
53 DOMAIN_SUSPEND = 2
55 """Map shutdown codes to strings."""
56 shutdown_reasons = {
57 DOMAIN_POWEROFF: "poweroff",
58 DOMAIN_REBOOT : "reboot",
59 DOMAIN_SUSPEND : "suspend" }
61 RESTART_ALWAYS = 'always'
62 RESTART_ONREBOOT = 'onreboot'
63 RESTART_NEVER = 'never'
65 restart_modes = [
66 RESTART_ALWAYS,
67 RESTART_ONREBOOT,
68 RESTART_NEVER,
69 ]
71 STATE_RESTART_PENDING = 'pending'
72 STATE_RESTART_BOOTING = 'booting'
74 STATE_VM_OK = "ok"
75 STATE_VM_TERMINATED = "terminated"
78 def domain_exists(name):
79 # See comment in XendDomain constructor.
80 xd = get_component('xen.xend.XendDomain')
81 return xd.domain_exists(name)
83 def shutdown_reason(code):
84 """Get a shutdown reason from a code.
86 @param code: shutdown code
87 @type code: int
88 @return: shutdown reason
89 @rtype: string
90 """
91 return shutdown_reasons.get(code, "?")
93 def blkdev_name_to_number(name):
94 """Take the given textual block-device name (e.g., '/dev/sda1',
95 'hda') and return the device number used by the OS. """
97 if not re.match( '^/dev/', name ):
98 n = '/dev/' + name
99 else:
100 n = name
102 try:
103 return os.stat(n).st_rdev
104 except:
105 pass
107 # see if this is a hex device number
108 if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
109 return string.atoi(name,16)
111 return None
113 def lookup_raw_partn(name):
114 """Take the given block-device name (e.g., '/dev/sda1', 'hda')
115 and return a dictionary { device, start_sector,
116 nr_sectors, type }
117 device: Device number of the given partition
118 start_sector: Index of first sector of the partition
119 nr_sectors: Number of sectors comprising this partition
120 type: 'Disk' or identifying name for partition type
121 """
123 n = blkdev_name_to_number(name)
124 if n:
125 return [ { 'device' : n,
126 'start_sector' : long(0),
127 'nr_sectors' : long(1L<<63),
128 'type' : 'Disk' } ]
129 else:
130 return None
132 def lookup_disk_uname(uname):
133 """Lookup a list of segments for a physical device.
135 @param uname: name of the device in the format \'phy:dev\' for a physical device
136 @type uname: string
137 @return: list of extents that make up the named device
138 @rtype: [dict]
139 """
140 ( type, d_name ) = string.split( uname, ':' )
142 if type == "phy":
143 segments = lookup_raw_partn( d_name )
144 else:
145 segments = None
146 return segments
148 def make_disk(vm, config, uname, dev, mode, recreate=0):
149 """Create a virtual disk device for a domain.
151 @param vm: vm
152 @param uname: device to export
153 @param dev: device name in domain
154 @param mode: read/write mode
155 @param recreate: recreate flag (after xend restart)
156 @return: deferred
157 """
158 idx = vm.next_device_index('vbd')
159 segments = lookup_disk_uname(uname)
160 if not segments:
161 raise VmError("vbd: Segments not found: uname=%s" % uname)
162 if len(segments) > 1:
163 raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
164 segment = segments[0]
165 # todo: The 'dev' should be looked up in the context of the domain.
166 vdev = blkdev_name_to_number(dev)
167 if not vdev:
168 raise VmError("vbd: Device not found: uname=%s dev=%s" % (uname, dev))
169 ctrl = xend.blkif_create(vm.dom, recreate=recreate)
170 return ctrl.attachDevice(idx, config, vdev, mode, segment, recreate=recreate)
172 def vif_up(iplist):
173 """send an unsolicited ARP reply for all non link-local IP addresses.
175 @param iplist: IP addresses
176 """
178 IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
180 def get_ip_nonlocal_bind():
181 return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
183 def set_ip_nonlocal_bind(v):
184 print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
186 def link_local(ip):
187 return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
189 def arping(ip, gw):
190 cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
191 log.debug(cmd)
192 os.system(cmd)
194 gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
195 nlb = get_ip_nonlocal_bind()
196 if not nlb: set_ip_nonlocal_bind(1)
197 try:
198 for ip in iplist:
199 if not link_local(ip):
200 arping(ip, gateway)
201 finally:
202 if not nlb: set_ip_nonlocal_bind(0)
204 config_handlers = {}
206 def add_config_handler(name, h):
207 """Add a handler for a config field.
209 @param name: field name
210 @param h: handler: fn(vm, config, field, index)
211 """
212 config_handlers[name] = h
214 def get_config_handler(name):
215 """Get a handler for a config field.
217 returns handler or None
218 """
219 return config_handlers.get(name)
221 """Table of handlers for virtual machine images.
222 Indexed by image type.
223 """
224 image_handlers = {}
226 def add_image_handler(name, h):
227 """Add a handler for an image type
228 @param name: image type
229 @param h: handler: fn(config, name, memory, image)
230 """
231 image_handlers[name] = h
233 def get_image_handler(name):
234 """Get the handler for an image type.
235 @param name: image type
236 @return: handler or None
237 """
238 return image_handlers.get(name)
240 """Table of handlers for devices.
241 Indexed by device type.
242 """
243 device_handlers = {}
245 def add_device_handler(name, h):
246 """Add a handler for a device type.
248 @param name: device type
249 @param h: handler: fn(vm, dev)
250 """
251 device_handlers[name] = h
253 def get_device_handler(name):
254 """Get the handler for a device type.
256 @param name : device type
257 @return; handler or None
258 """
259 return device_handlers.get(name)
261 def vm_create(config):
262 """Create a VM from a configuration.
263 If a vm has been partially created and there is an error it
264 is destroyed.
266 @param config configuration
267 @return: Deferred
268 @raise: VmError for invalid configuration
269 """
270 vm = XendDomainInfo()
271 return vm.construct(config)
273 def vm_recreate(savedinfo, info):
274 """Create the VM object for an existing domain.
276 @param savedinfo: saved info from the domain DB
277 @type savedinfo: sxpr
278 @param info: domain info from xc
279 @type info: xc domain dict
280 @return: deferred
281 """
282 vm = XendDomainInfo()
283 vm.recreate = 1
284 vm.setdom(info['dom'])
285 #vm.name = info['name']
286 vm.memory = info['mem_kb']/1024
287 start_time = sxp.child_value(savedinfo, 'start_time')
288 if start_time is not None:
289 vm.start_time = float(start_time)
290 vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
291 restart_time = sxp.child_value(savedinfo, 'restart_time')
292 if restart_time is not None:
293 vm.restart_time = float(restart_time)
294 config = sxp.child_value(savedinfo, 'config')
295 if config:
296 d = vm.construct(config)
297 else:
298 vm.name = info['name']
299 d = defer.succeed(vm)
300 vm.recreate = 0
301 return d
303 def vm_restore(src, progress=0):
304 """Restore a VM from a disk image.
306 src saved state to restore
307 progress progress reporting flag
308 returns deferred
309 raises VmError for invalid configuration
310 """
311 vm = XendDomainInfo()
312 ostype = "linux" #todo Set from somewhere (store in the src?).
313 restorefn = getattr(xc, "%s_restore" % ostype)
314 d = restorefn(state_file=src, progress=progress)
315 dom = int(d['dom'])
316 if dom < 0:
317 raise VmError('restore failed')
318 try:
319 vmconfig = sxp.from_string(d['vmconfig'])
320 config = sxp.child_value(vmconfig, 'config')
321 except Exception, ex:
322 raise VmError('config error: ' + str(ex))
323 deferred = vm.dom_construct(dom, config)
324 def vifs_cb(val, vm):
325 vif_up(vm.ipaddrs)
326 return vm
327 deferred.addCallback(vifs_cb, vm)
328 return deferred
330 def dom_get(dom):
331 """Get info from xen for an existing domain.
333 @param dom: domain id
334 @return: info or None
335 """
336 domlist = xc.domain_getinfo(dom, 1)
337 if domlist and dom == domlist[0]['dom']:
338 return domlist[0]
339 return None
341 def append_deferred(dlist, v):
342 """Append a value to a deferred list if it is a deferred.
344 @param dlist: list of deferreds
345 @param v: value to add
346 """
347 if isinstance(v, defer.Deferred):
348 dlist.append(v)
350 class XendDomainInfo:
351 """Virtual machine object."""
353 """Minimum time between domain restarts in seconds.
354 """
355 MINIMUM_RESTART_TIME = 20
357 def __init__(self):
358 self.recreate = 0
359 self.restore = 0
360 self.config = None
361 self.id = None
362 self.dom = None
363 self.cpu_weight = 1
364 self.start_time = None
365 self.name = None
366 self.memory = None
367 self.image = None
368 self.ramdisk = None
369 self.cmdline = None
370 self.console = None
371 self.devices = {}
372 self.device_index = {}
373 self.configs = []
374 self.info = None
375 self.ipaddrs = []
376 self.blkif_backend = 0
377 self.netif_backend = 0
378 #todo: state: running, suspended
379 self.state = STATE_VM_OK
380 #todo: set to migrate info if migrating
381 self.migrate = None
382 self.restart_mode = RESTART_ONREBOOT
383 self.restart_state = None
384 self.restart_time = None
385 self.console_port = None
387 def setdom(self, dom):
388 """Set the domain id.
390 @param dom: domain id
391 """
392 self.dom = int(dom)
393 self.id = str(dom)
395 def update(self, info):
396 """Update with info from xc.domain_getinfo().
397 """
398 self.info = info
399 self.memory = self.info['mem_kb'] / 1024
401 def __str__(self):
402 s = "domain"
403 s += " id=" + self.id
404 s += " name=" + self.name
405 s += " memory=" + str(self.memory)
406 if self.console:
407 s += " console=" + str(self.console.console_port)
408 if self.image:
409 s += " image=" + self.image
410 s += ""
411 return s
413 __repr__ = __str__
415 def sxpr(self):
416 sxpr = ['domain',
417 ['id', self.id],
418 ['name', self.name],
419 ['memory', self.memory] ]
421 if self.info:
422 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
423 run = (self.info['running'] and 'r') or '-'
424 block = (self.info['blocked'] and 'b') or '-'
425 pause = (self.info['paused'] and 'p') or '-'
426 shut = (self.info['shutdown'] and 's') or '-'
427 crash = (self.info['crashed'] and 'c') or '-'
428 state = run + block + pause + shut + crash
429 sxpr.append(['state', state])
430 if self.info['shutdown']:
431 reason = shutdown_reason(self.info['shutdown_reason'])
432 sxpr.append(['shutdown_reason', reason])
433 sxpr.append(['cpu', self.info['cpu']])
434 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
436 if self.start_time:
437 up_time = time.time() - self.start_time
438 sxpr.append(['up_time', str(up_time) ])
439 sxpr.append(['start_time', str(self.start_time) ])
441 if self.console:
442 sxpr.append(self.console.sxpr())
443 if self.restart_state:
444 sxpr.append(['restart_state', self.restart_state])
445 if self.restart_time:
446 sxpr.append(['restart_time', str(self.restart_time)])
447 if self.config:
448 sxpr.append(['config', self.config])
449 return sxpr
451 def check_name(self, name):
452 """Check if a vm name is valid. Valid names start with a non-digit
453 and contain alphabetic characters, digits, or characters in '_-.:/+'.
454 The same name cannot be used for more than one vm at the same time.
456 @param name: name
457 @raise: VMerror if invalid
458 """
459 if self.recreate: return
460 if name is None or name == '':
461 raise VmError('missing vm name')
462 if name[0] in string.digits:
463 raise VmError('invalid vm name')
464 for c in name:
465 if c in string.digits: continue
466 if c in '_-.:/+': continue
467 if c in string.ascii_letters: continue
468 raise VmError('invalid vm name')
469 dominfo = domain_exists(name)
470 # When creating or rebooting, a domain with my name should not exist.
471 # When restoring, a domain with my name will exist, but it should have
472 # my domain id.
473 if not dominfo:
474 return
475 if dominfo.is_terminated():
476 return
477 if not self.dom or (dominfo.dom != self.dom):
478 raise VmError('vm name clash: ' + name)
480 def construct(self, config):
481 """Construct the vm instance from its configuration.
483 @param config: configuration
484 @return: deferred
485 @raise: VmError on error
486 """
487 # todo - add support for scheduling params?
488 self.config = config
489 try:
490 self.name = sxp.child_value(config, 'name')
491 self.check_name(self.name)
492 try:
493 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
494 except:
495 raise VmError('invalid cpu weight')
496 if self.restore and self.dom:
497 xc.domain_setname(self.dom, self.name)
498 self.memory = int(sxp.child_value(config, 'memory'))
499 if self.memory is None:
500 raise VmError('missing memory size')
502 self.init_domain()
503 self.configure_console()
504 self.construct_image()
505 self.configure_restart()
506 self.configure_backends()
507 deferred = self.configure()
508 def cberr(err):
509 self.destroy()
510 return err
511 deferred.addErrback(cberr)
512 except StandardError, ex:
513 # Catch errors, cleanup and re-raise.
514 self.destroy()
515 raise
516 return deferred
518 def construct_image(self):
519 """Construct the boot image for the domain.
521 @return vm
522 """
523 image = sxp.child_value(self.config, 'image')
524 if image is None:
525 raise VmError('missing image')
526 image_name = sxp.name(image)
527 if image_name is None:
528 raise VmError('missing image name')
529 image_handler = get_image_handler(image_name)
530 if image_handler is None:
531 raise VmError('unknown image type: ' + image_name)
532 image_handler(self, image)
533 return self
535 def config_devices(self, name):
536 """Get a list of the 'device' nodes of a given type from the config.
538 @param name: device type
539 @type name: string
540 @return: device configs
541 @rtype: list
542 """
543 devices = []
544 for d in sxp.children(self.config, 'device'):
545 dev = sxp.child0(d)
546 if dev is None: continue
547 if name == sxp.name(dev):
548 devices.append(dev)
549 return devices
551 def config_device(self, type, idx):
552 """Get a device config from the device nodes of a given type
553 from the config.
555 @param type: device type
556 @type type: string
557 @param idx: index
558 @type idx: int
559 @return config or None
560 """
561 devs = self.config_devices(type)
562 if 0 <= idx < len(devs):
563 return devs[idx]
564 else:
565 return None
567 def next_device_index(self, type):
568 """Get the next index for a given device type.
570 @param type: device type
571 @type type: string
572 @return device index
573 @rtype: int
574 """
575 idx = self.device_index.get(type, 0)
576 self.device_index[type] = idx + 1
577 return idx
579 def add_device(self, type, dev):
580 """Add a device to a virtual machine.
582 @param type: device type
583 @param dev: device to add
584 """
585 dl = self.devices.get(type, [])
586 dl.append(dev)
587 self.devices[type] = dl
589 def remove_device(self, type, dev):
590 """Remove a device from a virtual machine.
592 @param type: device type
593 @param dev: device
594 """
595 dl = self.devices.get(type, [])
596 if dev in dl:
597 dl.remove(dev)
599 def get_devices(self, type):
600 """Get a list of the devices of a given type.
602 @param type: device type
603 @return: devices
604 """
605 val = self.devices.get(type, [])
606 return val
608 def get_device_by_id(self, type, id):
609 """Get the device with the given id.
611 @param id: device id
612 @return: device or None
613 """
614 dl = self.get_devices(type)
615 for d in dl:
616 if d.getprop('id') == id:
617 return d
618 return None
620 def get_device_by_index(self, type, idx):
621 """Get the device with the given index.
623 @param idx: device index
624 @return: device or None
625 """
626 idx = str(idx)
627 dl = self.get_devices(type)
628 for d in dl:
629 if d.getidx() == idx:
630 return d
631 return None
633 def add_config(self, val):
634 """Add configuration data to a virtual machine.
636 @param val: data to add
637 """
638 self.configs.append(val)
640 def destroy(self):
641 """Completely destroy the vm.
642 """
643 self.cleanup()
644 return self.destroy_domain()
646 def destroy_domain(self):
647 """Destroy the vm's domain.
648 The domain will not finally go away unless all vm
649 devices have been released.
650 """
651 if self.dom is None: return 0
652 self.destroy_console()
653 chan = xend.getDomChannel(self.dom)
654 if chan:
655 log.debug("Closing channel to domain %d", self.dom)
656 chan.close()
657 try:
658 return xc.domain_destroy(dom=self.dom)
659 except Exception, err:
660 log.exception("Domain destroy failed: %s", self.name)
662 def destroy_console(self):
663 if self.console:
664 if self.restart_pending():
665 self.console.deregisterChannel()
666 else:
667 log.debug('Closing console, domain %s', self.id)
668 self.console.close()
670 def cleanup(self):
671 """Cleanup vm resources: release devices.
672 """
673 self.state = STATE_VM_TERMINATED
674 self.release_devices()
676 def is_terminated(self):
677 """Check if a domain has been terminated.
678 """
679 return self.state == STATE_VM_TERMINATED
681 def release_devices(self):
682 """Release all vm devices.
683 """
684 self.release_vifs()
685 self.release_vbds()
687 self.devices = {}
688 self.device_index = {}
689 self.configs = []
690 self.ipaddrs = []
692 def release_vifs(self):
693 """Release vm virtual network devices (vifs).
694 """
695 if self.dom is None: return
696 ctrl = xend.netif_get(self.dom)
697 if ctrl:
698 log.debug("Destroying vifs for domain %d", self.dom)
699 ctrl.destroy()
701 def release_vbds(self):
702 """Release vm virtual block devices (vbds).
703 """
704 if self.dom is None: return
705 ctrl = xend.blkif_get(self.dom)
706 if ctrl:
707 log.debug("Destroying vbds for domain %d", self.dom)
708 ctrl.destroy()
710 def show(self):
711 """Print virtual machine info.
712 """
713 print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
714 print "image:"
715 sxp.show(self.image)
716 print
717 for dl in self.devices:
718 for dev in dl:
719 print "device:"
720 sxp.show(dev)
721 print
722 for val in self.configs:
723 print "config:"
724 sxp.show(val)
725 print
726 print "]"
728 def init_domain(self):
729 """Initialize the domain memory.
730 """
731 if self.recreate:
732 return
733 if self.start_time is None:
734 self.start_time = time.time()
735 if self.restore:
736 return
737 dom = self.dom or 0
738 memory = self.memory
739 name = self.name
740 # If the name is over the xen limit, use the end of it.
741 if len(name) > MAX_DOMAIN_NAME:
742 name = name[-MAX_DOMAIN_NAME:]
743 try:
744 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
745 except:
746 raise VmError('invalid cpu')
747 cpu_weight = self.cpu_weight
748 dom = xc.domain_create(dom= dom, mem_kb= memory * 1024,
749 name= name, cpu= cpu, cpu_weight= cpu_weight)
750 if dom <= 0:
751 raise VmError('Creating domain failed: name=%s memory=%d'
752 % (name, memory))
753 log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
754 self.setdom(dom)
756 def build_domain(self, ostype, kernel, ramdisk, cmdline):
757 """Build the domain boot image.
758 """
759 if self.recreate or self.restore: return
760 if len(cmdline) >= 256:
761 log.warning('kernel cmdline too long, domain %d', self.dom)
762 dom = self.dom
763 buildfn = getattr(xc, '%s_build' % ostype)
764 flags = 0
765 if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
766 if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
767 err = buildfn(dom = dom,
768 image = kernel,
769 control_evtchn = self.console.getRemotePort(),
770 cmdline = cmdline,
771 ramdisk = ramdisk,
772 flags = flags)
773 if err != 0:
774 raise VmError('Building domain failed: type=%s dom=%d err=%d'
775 % (ostype, dom, err))
777 def create_domain(self, ostype, kernel, ramdisk, cmdline):
778 """Create a domain. Builds the image but does not configure it.
780 @param ostype: OS type
781 @param kernel: kernel image
782 @param ramdisk: kernel ramdisk
783 @param cmdline: kernel commandline
784 """
785 if not self.recreate:
786 if not os.path.isfile(kernel):
787 raise VmError('Kernel image does not exist: %s' % kernel)
788 if ramdisk and not os.path.isfile(ramdisk):
789 raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
790 #self.init_domain()
791 if self.console:
792 self.console.registerChannel()
793 else:
794 self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
795 self.build_domain(ostype, kernel, ramdisk, cmdline)
796 self.image = kernel
797 self.ramdisk = ramdisk
798 self.cmdline = cmdline
800 def create_devices(self):
801 """Create the devices for a vm.
803 @return: Deferred
804 @raise: VmError for invalid devices
805 """
806 dlist = []
807 devices = sxp.children(self.config, 'device')
808 index = {}
809 for d in devices:
810 dev = sxp.child0(d)
811 if dev is None:
812 raise VmError('invalid device')
813 dev_name = sxp.name(dev)
814 dev_index = index.get(dev_name, 0)
815 dev_handler = get_device_handler(dev_name)
816 if dev_handler is None:
817 raise VmError('unknown device type: ' + dev_name)
818 v = dev_handler(self, dev, dev_index)
819 append_deferred(dlist, v)
820 index[dev_name] = dev_index + 1
821 deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
822 return deferred
824 def device_create(self, dev_config):
825 """Create a new device.
827 @param dev_config: device configuration
828 @return: deferred
829 """
830 dev_name = sxp.name(dev_config)
831 dev_handler = get_device_handler(dev_name)
832 if dev_handler is None:
833 raise VmError('unknown device type: ' + dev_name)
834 devs = self.get_devices(dev_name)
835 dev_index = len(devs)
836 self.config.append(['device', dev_config])
837 d = dev_handler(self, dev_config, dev_index, change=1)
838 def cbok(dev):
839 return dev.sxpr()
840 d.addCallback(cbok)
841 return d
843 def device_configure(self, dev_config, idx):
844 """Configure an existing device.
846 @param dev_config: device configuration
847 @param idx: device index
848 """
849 type = sxp.name(dev_config)
850 dev = self.get_device_by_index(type, idx)
851 if not dev:
852 raise VmError('invalid device: %s %s' % (type, idx))
853 new_config = dev.configure(dev_config, change=1)
854 devs = self.devices.get(type)
855 index = devs.index(dev)
856 # Patch new config into device configs.
857 dev_configs = self.config_devices(type)
858 old_config = dev_configs[index]
859 dev_configs[index] = new_config
860 # Patch new config into vm config.
861 new_full_config = ['device', new_config]
862 old_full_config = ['device', old_config]
863 old_index = self.config.index(old_full_config)
864 self.config[old_index] = new_full_config
865 return new_config
867 def device_destroy(self, type, idx):
868 """Destroy a device.
870 @param type: device type
871 @param idx: device index
872 """
873 dev = self.get_device_by_index(type, idx)
874 if not dev:
875 raise VmError('invalid device: %s %s' % (type, idx))
876 devs = self.devices.get(type)
877 index = devs.index(dev)
878 dev_config = self.config_device(type, index)
879 if dev_config:
880 self.config.remove(['device', dev_config])
881 dev.destroy(change=1)
882 self.remove_device(type, dev)
884 def configure_memory(self):
885 """Configure vm memory limit.
886 """
887 maxmem = sxp.get_child_value(self.config, "maxmem")
888 if maxmem is None:
889 maxmem = self.memory
890 xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
892 def configure_console(self):
893 """Configure the vm console port.
894 """
895 x = sxp.child_value(self.config, 'console')
896 if x:
897 try:
898 port = int(x)
899 except:
900 raise VmError('invalid console:' + str(x))
901 self.console_port = port
903 def configure_restart(self):
904 """Configure the vm restart mode.
905 """
906 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
907 if r not in restart_modes:
908 raise VmError('invalid restart mode: ' + str(r))
909 self.restart_mode = r;
911 def restart_needed(self, reason):
912 """Determine if the vm needs to be restarted when shutdown
913 for the given reason.
915 @param reason: shutdown reason
916 @return 1 if needs restaert, 0 otherwise
917 """
918 if self.restart_mode == RESTART_NEVER:
919 return 0
920 if self.restart_mode == RESTART_ALWAYS:
921 return 1
922 if self.restart_mode == RESTART_ONREBOOT:
923 return reason == 'reboot'
924 return 0
926 def restart_cancel(self):
927 """Cancel a vm restart.
928 """
929 self.restart_state = None
931 def restarting(self):
932 """Put the vm into restart mode.
933 """
934 self.restart_state = STATE_RESTART_PENDING
936 def restart_pending(self):
937 """Test if the vm has a pending restart.
938 """
939 return self.restart_state == STATE_RESTART_PENDING
941 def restart_check(self):
942 """Check if domain restart is OK.
943 To prevent restart loops, raise an error if it is
944 less than MINIMUM_RESTART_TIME seconds since the last restart.
945 """
946 tnow = time.time()
947 if self.restart_time is not None:
948 tdelta = tnow - self.restart_time
949 if tdelta < self.MINIMUM_RESTART_TIME:
950 self.restart_cancel()
951 msg = 'VM %s restarting too fast' % self.name
952 log.error(msg)
953 raise VmError(msg)
954 self.restart_time = tnow
956 def restart(self):
957 """Restart the domain after it has exited.
958 Reuses the domain id and console port.
960 @return: deferred
961 """
962 try:
963 self.restart_check()
964 self.restart_state = STATE_RESTART_BOOTING
965 d = self.construct(self.config)
966 finally:
967 self.restart_state = None
968 return d
970 def configure_backends(self):
971 """Set configuration flags if the vm is a backend for netif or blkif.
972 Configure the backends to use for vbd and vif if specified.
973 """
974 for c in sxp.children(self.config, 'backend'):
975 v = sxp.child0(c)
976 name = sxp.name(v)
977 if name == 'blkif':
978 self.blkif_backend = 1
979 elif name == 'netif':
980 self.netif_backend = 1
981 else:
982 raise VmError('invalid backend type:' + str(name))
984 def configure(self):
985 """Configure a vm.
987 @return: deferred - calls callback with vm
988 """
989 d = self.create_blkif()
990 d.addCallback(lambda x: self.create_devices())
991 d.addCallback(self._configure)
992 return d
994 def _configure(self, val):
995 d = self.configure_fields()
996 def cbok(results):
997 return self
998 def cberr(err):
999 self.destroy()
1000 return err
1001 d.addCallback(cbok)
1002 d.addErrback(cberr)
1003 return d
1005 def create_blkif(self):
1006 """Create the block device interface (blkif) for the vm.
1007 The vm needs a blkif even if it doesn't have any disks
1008 at creation time, for example when it uses NFS root.
1010 @return: deferred
1011 """
1012 ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
1013 back = ctrl.getBackendInterface(0)
1014 return back.connect(recreate=self.recreate)
1016 def dom_construct(self, dom, config):
1017 """Construct a vm for an existing domain.
1019 @param dom: domain id
1020 @param config: domain configuration
1021 @return: deferred
1022 """
1023 d = dom_get(dom)
1024 if not d:
1025 raise VmError("Domain not found: %d" % dom)
1026 try:
1027 self.restore = 1
1028 self.setdom(dom)
1029 #self.name = d['name']
1030 self.memory = d['mem_kb']/1024
1031 deferred = self.construct(config)
1032 finally:
1033 self.restore = 0
1034 return deferred
1036 def configure_fields(self):
1037 """Process the vm configuration fields using the registered handlers.
1038 """
1039 dlist = []
1040 index = {}
1041 for field in sxp.children(self.config):
1042 field_name = sxp.name(field)
1043 field_index = index.get(field_name, 0)
1044 field_handler = get_config_handler(field_name)
1045 # Ignore unknown fields. Warn?
1046 if field_handler:
1047 v = field_handler(self, self.config, field, field_index)
1048 append_deferred(dlist, v)
1049 else:
1050 log.warning("Unknown config field %s", field_name)
1051 index[field_name] = field_index + 1
1052 d = defer.DeferredList(dlist, fireOnOneErrback=1)
1053 return d
1056 def vm_image_linux(vm, image):
1057 """Create a VM for a linux image.
1059 @param name: vm name
1060 @param memory: vm memory
1061 @param image: image config
1062 @return: vm
1063 """
1064 kernel = sxp.child_value(image, "kernel")
1065 cmdline = ""
1066 ip = sxp.child_value(image, "ip", "dhcp")
1067 if ip:
1068 cmdline += " ip=" + ip
1069 root = sxp.child_value(image, "root")
1070 if root:
1071 cmdline += " root=" + root
1072 args = sxp.child_value(image, "args")
1073 if args:
1074 cmdline += " " + args
1075 ramdisk = sxp.child_value(image, "ramdisk", '')
1076 vm.create_domain("linux", kernel, ramdisk, cmdline)
1077 return vm
1079 def vm_image_netbsd(vm, image):
1080 """Create a VM for a bsd image.
1082 @param name: vm name
1083 @param memory: vm memory
1084 @param image: image config
1085 @return: vm
1086 """
1087 #todo: Same as for linux. Is that right? If so can unify them.
1088 kernel = sxp.child_value(image, "kernel")
1089 cmdline = ""
1090 ip = sxp.child_value(image, "ip", "dhcp")
1091 if ip:
1092 cmdline += "ip=" + ip
1093 root = sxp.child_value(image, "root")
1094 if root:
1095 cmdline += "root=" + root
1096 args = sxp.child_value(image, "args")
1097 if args:
1098 cmdline += " " + args
1099 ramdisk = sxp.child_value(image, "ramdisk", '')
1100 vm.create_domain("netbsd", kernel, ramdisk, cmdline)
1101 return vm
1104 def vm_dev_vif(vm, val, index, change=0):
1105 """Create a virtual network interface (vif).
1107 @param vm: virtual machine
1108 @param val: vif config
1109 @param index: vif index
1110 @return: deferred
1111 """
1112 vif = vm.next_device_index('vif')
1113 vmac = sxp.child_value(val, "mac")
1114 ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
1115 log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
1116 defer = ctrl.attachDevice(vif, val, recreate=vm.recreate)
1117 def cbok(dev):
1118 dev.vifctl('up', vmname=vm.name)
1119 vm.add_device('vif', dev)
1120 if change:
1121 dev.interfaceChanged()
1122 return dev
1123 defer.addCallback(cbok)
1124 return defer
1126 def vm_dev_vbd(vm, val, index, change=0):
1127 """Create a virtual block device (vbd).
1129 @param vm: virtual machine
1130 @param val: vbd config
1131 @param index: vbd index
1132 @return: deferred
1133 """
1134 uname = sxp.child_value(val, 'uname')
1135 if not uname:
1136 raise VmError('vbd: Missing uname')
1137 dev = sxp.child_value(val, 'dev')
1138 if not dev:
1139 raise VmError('vbd: Missing dev')
1140 mode = sxp.child_value(val, 'mode', 'r')
1141 log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
1142 defer = make_disk(vm, val, uname, dev, mode, vm.recreate)
1143 def fn(vbd):
1144 vbd.dev = dev
1145 vbd.uname = uname
1146 vm.add_device('vbd', vbd)
1147 if change:
1148 vbd.interfaceChanged()
1149 return vbd
1150 defer.addCallback(fn)
1151 return defer
1153 def parse_pci(val):
1154 """Parse a pci field.
1155 """
1156 if isinstance(val, types.StringType):
1157 radix = 10
1158 if val.startswith('0x') or val.startswith('0X'):
1159 radix = 16
1160 v = int(val, radix)
1161 else:
1162 v = val
1163 return v
1165 def vm_dev_pci(vm, val, index, change=0):
1166 """Add a pci device.
1168 @param vm: virtual machine
1169 @param val: device configuration
1170 @param index: device index
1171 @return: 0 on success
1172 """
1173 bus = sxp.child_value(val, 'bus')
1174 if not bus:
1175 raise VmError('pci: Missing bus')
1176 dev = sxp.child_value(val, 'dev')
1177 if not dev:
1178 raise VmError('pci: Missing dev')
1179 func = sxp.child_value(val, 'func')
1180 if not func:
1181 raise VmError('pci: Missing func')
1182 try:
1183 bus = parse_pci(bus)
1184 dev = parse_pci(dev)
1185 func = parse_pci(func)
1186 except:
1187 raise VmError('pci: invalid parameter')
1188 log.debug("Creating pci device dom=%d bus=%x dev=%x func=%x", vm.dom, bus, dev, func)
1189 rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
1190 func=func, enable=1)
1191 if rc < 0:
1192 #todo non-fatal
1193 raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
1194 (bus, dev, func))
1195 return rc
1198 def vm_field_ignore(vm, config, val, index):
1199 """Dummy config field handler used for fields with built-in handling.
1201 @param vm: virtual machine
1202 @param config: vm config
1203 @param val: config field
1204 @param index: field index
1205 """
1206 pass
1208 def vm_field_maxmem(vm, config, val, index):
1209 """Configure vm memory limit.
1211 @param vm: virtual machine
1212 @param config: vm config
1213 @param val: config field
1214 @param index: field index
1215 """
1216 maxmem = sxp.child0(val)
1217 if maxmem is None:
1218 maxmem = vm.memory
1219 try:
1220 maxmem = int(maxmem)
1221 except:
1222 raise VmError("invalid maxmem: " + str(maxmem))
1223 xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
1225 # Register image handlers.
1226 add_image_handler('linux', vm_image_linux)
1227 add_image_handler('netbsd', vm_image_netbsd)
1229 # Register device handlers.
1230 add_device_handler('vif', vm_dev_vif)
1231 add_device_handler('vbd', vm_dev_vbd)
1232 add_device_handler('pci', vm_dev_pci)
1234 # Ignore the fields we already handle.
1235 add_config_handler('name', vm_field_ignore)
1236 add_config_handler('memory', vm_field_ignore)
1237 add_config_handler('cpu', vm_field_ignore)
1238 add_config_handler('cpu_weight', vm_field_ignore)
1239 add_config_handler('console', vm_field_ignore)
1240 add_config_handler('image', vm_field_ignore)
1241 add_config_handler('device', vm_field_ignore)
1242 add_config_handler('backend', vm_field_ignore)
1244 # Register other config handlers.
1245 add_config_handler('maxmem', vm_field_maxmem)