ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 10108:e7d7287ab222

[XEND] separate concept of initial memory size and overhead memory size.

When a domain (whether para- or fully-virtualized) reports how much
overhead memory it requires (via getDomainMemory in image.py), all such
memory was immediately allocated to the domain itself. This is
certainly incorrect for HVM domains, since additional
increase_reservation calls are made later in qemu. Since all ballooned
memory is already taken, qemu will fail. The fix is to treat the
requested memory size and the overhead size as separate values. The
requested memory size is immediately allocated to the new domain; the
overhead is left unallocated for whatever else might need it later.

Signed-off-by: Charles Coffing <ccoffing@novell.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri May 19 16:07:36 2006 +0100 (2006-05-19)
parents 36e0159c001b
children 13d6d993d797
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005, 2006 XenSource Ltd
17 #============================================================================
19 """Representation of a single domain.
20 Includes support for domain construction, using
21 open-ended configurations.
23 Author: Mike Wray <mike.wray@hp.com>
25 """
27 import errno
28 import logging
29 import string
30 import time
31 import threading
33 import xen.lowlevel.xc
34 from xen.util import asserts
35 from xen.util.blkif import blkdev_uname_to_file
36 from xen.util import security
37 import balloon
38 import image
39 import sxp
40 import uuid
41 import XendDomain
42 import XendRoot
44 from xen.xend.XendBootloader import bootloader
45 from xen.xend.XendError import XendError, VmError
47 from xen.xend.xenstore.xstransact import xstransact, complete
48 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain
49 from xen.xend.xenstore.xswatch import xswatch
52 """Shutdown code for poweroff."""
53 DOMAIN_POWEROFF = 0
55 """Shutdown code for reboot."""
56 DOMAIN_REBOOT = 1
58 """Shutdown code for suspend."""
59 DOMAIN_SUSPEND = 2
61 """Shutdown code for crash."""
62 DOMAIN_CRASH = 3
64 """Shutdown code for halt."""
65 DOMAIN_HALT = 4
67 """Map shutdown codes to strings."""
68 shutdown_reasons = {
69 DOMAIN_POWEROFF: "poweroff",
70 DOMAIN_REBOOT : "reboot",
71 DOMAIN_SUSPEND : "suspend",
72 DOMAIN_CRASH : "crash",
73 DOMAIN_HALT : "halt"
74 }
76 restart_modes = [
77 "restart",
78 "destroy",
79 "preserve",
80 "rename-restart"
81 ]
83 STATE_DOM_OK = 1
84 STATE_DOM_SHUTDOWN = 2
86 SHUTDOWN_TIMEOUT = 30.0
88 ZOMBIE_PREFIX = 'Zombie-'
90 """Constants for the different stages of ext. device migration """
91 DEV_MIGRATE_TEST = 0
92 DEV_MIGRATE_STEP1 = 1
93 DEV_MIGRATE_STEP2 = 2
94 DEV_MIGRATE_STEP3 = 3
96 """Minimum time between domain restarts in seconds."""
97 MINIMUM_RESTART_TIME = 20
99 RESTART_IN_PROGRESS = 'xend/restart_in_progress'
102 xc = xen.lowlevel.xc.xc()
103 xroot = XendRoot.instance()
105 log = logging.getLogger("xend.XendDomainInfo")
106 #log.setLevel(logging.TRACE)
109 ##
110 # All parameters of VMs that may be configured on-the-fly, or at start-up.
111 #
112 VM_CONFIG_PARAMS = [
113 ('name', str),
114 ('on_poweroff', str),
115 ('on_reboot', str),
116 ('on_crash', str),
117 ]
120 ##
121 # Configuration entries that we expect to round-trip -- be read from the
122 # config file or xc, written to save-files (i.e. through sxpr), and reused as
123 # config on restart or restore, all without munging. Some configuration
124 # entries are munged for backwards compatibility reasons, or because they
125 # don't come out of xc in the same form as they are specified in the config
126 # file, so those are handled separately.
127 ROUNDTRIPPING_CONFIG_ENTRIES = [
128 ('uuid', str),
129 ('vcpus', int),
130 ('vcpu_avail', int),
131 ('cpu_weight', float),
132 ('memory', int),
133 ('maxmem', int),
134 ('bootloader', str),
135 ('bootloader_args', str),
136 ('features', str),
137 ]
139 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
142 ##
143 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
144 # entries written to the store that cannot be reconfigured on-the-fly.
145 #
146 VM_STORE_ENTRIES = [
147 ('uuid', str),
148 ('vcpus', int),
149 ('vcpu_avail', int),
150 ('memory', int),
151 ('maxmem', int),
152 ('start_time', float),
153 ]
155 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
158 #
159 # There are a number of CPU-related fields:
160 #
161 # vcpus: the number of virtual CPUs this domain is configured to use.
162 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
163 # its VCPUs. This is translated to
164 # <dompath>/cpu/<id>/availability = {online,offline} for use
165 # by the guest domain.
166 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
167 # CPUs that that VCPU may use.
168 # cpu: a configuration setting requesting that VCPU 0 is pinned to
169 # the specified physical CPU.
170 #
171 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
172 # across save, restore, migrate, and restart). The other settings are only
173 # specific to the domain, so are lost when the VM moves.
174 #
177 def create(config):
178 """Create a VM from a configuration.
180 @param config configuration
181 @raise: VmError for invalid configuration
182 """
184 log.debug("XendDomainInfo.create(%s)", config)
186 vm = XendDomainInfo(parseConfig(config))
187 try:
188 vm.construct()
189 vm.initDomain()
190 vm.storeVmDetails()
191 vm.storeDomDetails()
192 vm.registerWatches()
193 vm.refreshShutdown()
194 return vm
195 except:
196 log.exception('Domain construction failed')
197 vm.destroy()
198 raise
201 def recreate(xeninfo, priv):
202 """Create the VM object for an existing domain. The domain must not
203 be dying, as the paths in the store should already have been removed,
204 and asking us to recreate them causes problems."""
206 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
208 assert not xeninfo['dying']
210 domid = xeninfo['dom']
211 uuid1 = xeninfo['handle']
212 xeninfo['uuid'] = uuid.toString(uuid1)
213 dompath = GetDomainPath(domid)
214 if not dompath:
215 raise XendError(
216 'No domain path in store for existing domain %d' % domid)
218 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
219 try:
220 vmpath = xstransact.Read(dompath, "vm")
221 if not vmpath:
222 raise XendError(
223 'No vm path in store for existing domain %d' % domid)
224 uuid2_str = xstransact.Read(vmpath, "uuid")
225 if not uuid2_str:
226 raise XendError(
227 'No vm/uuid path in store for existing domain %d' % domid)
229 uuid2 = uuid.fromString(uuid2_str)
231 if uuid1 != uuid2:
232 raise XendError(
233 'Uuid in store does not match uuid for existing domain %d: '
234 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
236 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
238 except Exception, exn:
239 if priv:
240 log.warn(str(exn))
242 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
243 vm.recreateDom()
244 vm.removeVm()
245 vm.storeVmDetails()
246 vm.storeDomDetails()
248 vm.registerWatches()
249 vm.refreshShutdown(xeninfo)
250 return vm
253 def restore(config):
254 """Create a domain and a VM object to do a restore.
256 @param config: domain configuration
257 """
259 log.debug("XendDomainInfo.restore(%s)", config)
261 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
262 try:
263 vm.construct()
264 vm.storeVmDetails()
265 vm.createDevices()
266 vm.createChannels()
267 vm.storeDomDetails()
268 vm.endRestore()
269 return vm
270 except:
271 vm.destroy()
272 raise
275 def parseConfig(config):
276 def get_cfg(name, conv = None):
277 val = sxp.child_value(config, name)
279 if conv and not val is None:
280 try:
281 return conv(val)
282 except TypeError, exn:
283 raise VmError(
284 'Invalid setting %s = %s in configuration: %s' %
285 (name, val, str(exn)))
286 else:
287 return val
290 log.debug("parseConfig: config is %s", config)
292 result = {}
294 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
295 result[e[0]] = get_cfg(e[0], e[1])
297 result['cpu'] = get_cfg('cpu', int)
298 result['cpus'] = get_cfg('cpus', str)
299 result['image'] = get_cfg('image')
300 tmp_security = get_cfg('security')
301 if tmp_security:
302 result['security'] = tmp_security
304 try:
305 if result['image']:
306 v = sxp.child_value(result['image'], 'vcpus')
307 if result['vcpus'] is None and v is not None:
308 result['vcpus'] = int(v)
309 elif v is not None and int(v) != result['vcpus']:
310 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
311 ' Using %s VCPUs for VM %s.') %
312 (result['vcpus'], v, result['uuid']))
313 result['vcpus'] = int(v)
314 except TypeError, exn:
315 raise VmError(
316 'Invalid configuration setting: vcpus = %s: %s' %
317 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
319 try:
320 # support legacy config files with 'cpu' parameter
321 # NB: prepending to list to support previous behavior
322 # where 'cpu' parameter pinned VCPU0.
323 if result['cpu']:
324 if result['cpus']:
325 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
326 else:
327 result['cpus'] = str(result['cpu'])
329 # convert 'cpus' string to list of ints
330 # 'cpus' supports a list of ranges (0-3), seperated by
331 # commas, and negation, (^1).
332 # Precedence is settled by order of the string:
333 # "0-3,^1" -> [0,2,3]
334 # "0-3,^1,1" -> [0,1,2,3]
335 if result['cpus']:
336 cpus = []
337 for c in result['cpus'].split(','):
338 if c.find('-') != -1:
339 (x,y) = c.split('-')
340 for i in range(int(x),int(y)+1):
341 cpus.append(int(i))
342 else:
343 # remove this element from the list
344 if c[0] == '^':
345 cpus = [x for x in cpus if x != int(c[1:])]
346 else:
347 cpus.append(int(c))
349 result['cpus'] = cpus
351 except ValueError, exn:
352 raise VmError(
353 'Invalid configuration setting: cpus = %s: %s' %
354 (result['cpus'], exn))
356 result['backend'] = []
357 for c in sxp.children(config, 'backend'):
358 result['backend'].append(sxp.name(sxp.child0(c)))
360 result['device'] = []
361 for d in sxp.children(config, 'device'):
362 c = sxp.child0(d)
363 result['device'].append((sxp.name(c), c))
365 # Configuration option "restart" is deprecated. Parse it, but
366 # let on_xyz override it if they are present.
367 restart = get_cfg('restart')
368 if restart:
369 def handle_restart(event, val):
370 if result[event] is None:
371 result[event] = val
373 if restart == "onreboot":
374 handle_restart('on_poweroff', 'destroy')
375 handle_restart('on_reboot', 'restart')
376 handle_restart('on_crash', 'destroy')
377 elif restart == "always":
378 handle_restart('on_poweroff', 'restart')
379 handle_restart('on_reboot', 'restart')
380 handle_restart('on_crash', 'restart')
381 elif restart == "never":
382 handle_restart('on_poweroff', 'destroy')
383 handle_restart('on_reboot', 'destroy')
384 handle_restart('on_crash', 'destroy')
385 else:
386 log.warn("Ignoring malformed and deprecated config option "
387 "restart = %s", restart)
389 log.debug("parseConfig: result is %s", result)
390 return result
393 def domain_by_name(name):
394 return XendDomain.instance().domain_lookup_by_name_nr(name)
397 def shutdown_reason(code):
398 """Get a shutdown reason from a code.
400 @param code: shutdown code
401 @type code: int
402 @return: shutdown reason
403 @rtype: string
404 """
405 return shutdown_reasons.get(code, "?")
407 def dom_get(dom):
408 """Get info from xen for an existing domain.
410 @param dom: domain id
411 @return: info or None
412 """
413 try:
414 domlist = xc.domain_getinfo(dom, 1)
415 if domlist and dom == domlist[0]['dom']:
416 return domlist[0]
417 except Exception, err:
418 # ignore missing domain
419 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
420 return None
423 class XendDomainInfo:
425 def __init__(self, info, domid = None, dompath = None, augment = False,
426 priv = False, resume = False):
428 self.info = info
430 if not self.infoIsSet('uuid'):
431 self.info['uuid'] = uuid.toString(uuid.create())
433 if domid is not None:
434 self.domid = domid
435 elif 'dom' in info:
436 self.domid = int(info['dom'])
437 else:
438 self.domid = None
440 self.vmpath = XendDomain.VMROOT + self.info['uuid']
441 self.dompath = dompath
443 if augment:
444 self.augmentInfo(priv)
446 self.validateInfo()
448 self.image = None
449 self.security = None
450 self.store_port = None
451 self.store_mfn = None
452 self.console_port = None
453 self.console_mfn = None
455 self.vmWatch = None
456 self.shutdownWatch = None
458 self.shutdownStartTime = None
460 self.state = STATE_DOM_OK
461 self.state_updated = threading.Condition()
462 self.refresh_shutdown_lock = threading.Condition()
464 self.setResume(resume)
466 ## private:
468 def readVMDetails(self, params):
469 """Read the specified parameters from the store.
470 """
471 try:
472 return self.gatherVm(*params)
473 except ValueError:
474 # One of the int/float entries in params has a corresponding store
475 # entry that is invalid. We recover, because older versions of
476 # Xend may have put the entry there (memory/target, for example),
477 # but this is in general a bad situation to have reached.
478 log.exception(
479 "Store corrupted at %s! Domain %d's configuration may be "
480 "affected.", self.vmpath, self.domid)
481 return []
484 def storeChanged(self, _):
485 log.trace("XendDomainInfo.storeChanged");
487 changed = False
489 def f(x, y):
490 if y is not None and self.info[x[0]] != y:
491 self.info[x[0]] = y
492 changed = True
494 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
496 im = self.readVm('image')
497 current_im = self.info['image']
498 if (im is not None and
499 (current_im is None or sxp.to_string(current_im) != im)):
500 self.info['image'] = sxp.from_string(im)
501 changed = True
503 if changed:
504 # Update the domain section of the store, as this contains some
505 # parameters derived from the VM configuration.
506 self.storeDomDetails()
508 return 1
511 def augmentInfo(self, priv):
512 """Augment self.info, as given to us through {@link #recreate}, with
513 values taken from the store. This recovers those values known to xend
514 but not to the hypervisor.
515 """
516 def useIfNeeded(name, val):
517 if not self.infoIsSet(name) and val is not None:
518 self.info[name] = val
520 if priv:
521 entries = VM_STORE_ENTRIES[:]
522 entries.remove(('memory', int))
523 entries.remove(('maxmem', int))
524 else:
525 entries = VM_STORE_ENTRIES
526 entries.append(('image', str))
527 entries.append(('security', str))
529 map(lambda x, y: useIfNeeded(x[0], y), entries,
530 self.readVMDetails(entries))
532 device = []
533 for c in controllerClasses:
534 devconfig = self.getDeviceConfigurations(c)
535 if devconfig:
536 device.extend(map(lambda x: (c, x), devconfig))
537 useIfNeeded('device', device)
540 def validateInfo(self):
541 """Validate and normalise the info block. This has either been parsed
542 by parseConfig, or received from xc through recreate and augmented by
543 the current store contents.
544 """
545 def defaultInfo(name, val):
546 if not self.infoIsSet(name):
547 self.info[name] = val()
549 try:
550 defaultInfo('name', lambda: "Domain-%d" % self.domid)
551 defaultInfo('on_poweroff', lambda: "destroy")
552 defaultInfo('on_reboot', lambda: "restart")
553 defaultInfo('on_crash', lambda: "restart")
554 defaultInfo('features', lambda: "")
555 defaultInfo('cpu', lambda: None)
556 defaultInfo('cpus', lambda: [])
557 defaultInfo('cpu_weight', lambda: 1.0)
559 # some domains don't have a config file (e.g. dom0 )
560 # to set number of vcpus so we derive available cpus
561 # from max_vcpu_id which is present for running domains.
562 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
563 avail = int(self.info['max_vcpu_id'])+1
564 else:
565 avail = int(1)
567 defaultInfo('vcpus', lambda: avail)
568 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
569 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
570 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
572 defaultInfo('memory', lambda: 0)
573 defaultInfo('maxmem', lambda: 0)
574 defaultInfo('bootloader', lambda: None)
575 defaultInfo('bootloader_args', lambda: None)
576 defaultInfo('backend', lambda: [])
577 defaultInfo('device', lambda: [])
578 defaultInfo('image', lambda: None)
579 defaultInfo('security', lambda: None)
581 self.check_name(self.info['name'])
583 if isinstance(self.info['image'], str):
584 self.info['image'] = sxp.from_string(self.info['image'])
586 if isinstance(self.info['security'], str):
587 self.info['security'] = sxp.from_string(self.info['security'])
589 if self.info['memory'] == 0:
590 if self.infoIsSet('mem_kb'):
591 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
593 if self.info['maxmem'] < self.info['memory']:
594 self.info['maxmem'] = self.info['memory']
596 for (n, c) in self.info['device']:
597 if not n or not c or n not in controllerClasses:
598 raise VmError('invalid device (%s, %s)' %
599 (str(n), str(c)))
601 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
602 if self.info[event] not in restart_modes:
603 raise VmError('invalid restart event: %s = %s' %
604 (event, str(self.info[event])))
606 except KeyError, exn:
607 log.exception(exn)
608 raise VmError('Unspecified domain detail: %s' % exn)
611 def readVm(self, *args):
612 return xstransact.Read(self.vmpath, *args)
614 def writeVm(self, *args):
615 return xstransact.Write(self.vmpath, *args)
617 def removeVm(self, *args):
618 return xstransact.Remove(self.vmpath, *args)
620 def gatherVm(self, *args):
621 return xstransact.Gather(self.vmpath, *args)
624 ## public:
626 def storeVm(self, *args):
627 return xstransact.Store(self.vmpath, *args)
630 ## private:
632 def readDom(self, *args):
633 return xstransact.Read(self.dompath, *args)
635 def writeDom(self, *args):
636 return xstransact.Write(self.dompath, *args)
639 ## public:
641 def removeDom(self, *args):
642 return xstransact.Remove(self.dompath, *args)
644 def recreateDom(self):
645 complete(self.dompath, lambda t: self._recreateDom(t))
647 def _recreateDom(self, t):
648 t.remove()
649 t.mkdir()
650 t.set_permissions({ 'dom' : self.domid })
653 ## private:
655 def storeDom(self, *args):
656 return xstransact.Store(self.dompath, *args)
659 ## public:
661 def completeRestore(self, store_mfn, console_mfn):
663 log.debug("XendDomainInfo.completeRestore")
665 self.store_mfn = store_mfn
666 self.console_mfn = console_mfn
668 self.introduceDomain()
669 self.storeDomDetails()
670 self.registerWatches()
671 self.refreshShutdown()
673 log.debug("XendDomainInfo.completeRestore done")
676 def storeVmDetails(self):
677 to_store = {}
679 for k in VM_STORE_ENTRIES:
680 if self.infoIsSet(k[0]):
681 to_store[k[0]] = str(self.info[k[0]])
683 if self.infoIsSet('image'):
684 to_store['image'] = sxp.to_string(self.info['image'])
686 if self.infoIsSet('security'):
687 security = self.info['security']
688 to_store['security'] = sxp.to_string(security)
689 for idx in range(0, len(security)):
690 if security[idx][0] == 'access_control':
691 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
692 for aidx in range(1, len(security[idx])):
693 if security[idx][aidx][0] == 'label':
694 to_store['security/access_control/label'] = security[idx][aidx][1]
695 if security[idx][aidx][0] == 'policy':
696 to_store['security/access_control/policy'] = security[idx][aidx][1]
697 if security[idx][0] == 'ssidref':
698 to_store['security/ssidref'] = str(security[idx][1])
700 log.debug("Storing VM details: %s", to_store)
702 self.writeVm(to_store)
705 def storeDomDetails(self):
706 to_store = {
707 'domid': str(self.domid),
708 'vm': self.vmpath,
709 'name': self.info['name'],
710 'console/limit': str(xroot.get_console_limit() * 1024),
711 'memory/target': str(self.info['memory'] * 1024)
712 }
714 def f(n, v):
715 if v is not None:
716 to_store[n] = str(v)
718 f('console/port', self.console_port)
719 f('console/ring-ref', self.console_mfn)
720 f('store/port', self.store_port)
721 f('store/ring-ref', self.store_mfn)
723 to_store.update(self.vcpuDomDetails())
725 log.debug("Storing domain details: %s", to_store)
727 self.writeDom(to_store)
730 ## private:
732 def vcpuDomDetails(self):
733 def availability(n):
734 if self.info['vcpu_avail'] & (1 << n):
735 return 'online'
736 else:
737 return 'offline'
739 result = {}
740 for v in range(0, self.info['vcpus']):
741 result["cpu/%d/availability" % v] = availability(v)
742 return result
745 ## public:
747 def registerWatches(self):
748 """Register a watch on this VM's entries in the store, and the
749 domain's control/shutdown node, so that when they are changed
750 externally, we keep up to date. This should only be called by {@link
751 #create}, {@link #recreate}, or {@link #restore}, once the domain's
752 details have been written, but before the new instance is returned."""
753 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
754 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
755 self.handleShutdownWatch)
758 def getDomid(self):
759 return self.domid
761 def setName(self, name):
762 self.check_name(name)
763 self.info['name'] = name
764 self.storeVm("name", name)
766 def getName(self):
767 return self.info['name']
769 def getDomainPath(self):
770 return self.dompath
773 def getStorePort(self):
774 """For use only by image.py and XendCheckpoint.py."""
775 return self.store_port
778 def getConsolePort(self):
779 """For use only by image.py and XendCheckpoint.py"""
780 return self.console_port
782 def getFeatures(self):
783 """For use only by image.py."""
784 return self.info['features']
786 def getVCpuCount(self):
787 return self.info['vcpus']
790 def setVCpuCount(self, vcpus):
791 self.info['vcpu_avail'] = (1 << vcpus) - 1
792 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
793 self.writeDom(self.vcpuDomDetails())
795 def getLabel(self):
796 return security.get_security_info(self.info, 'label')
798 def getMemoryTarget(self):
799 """Get this domain's target memory size, in KB."""
800 return self.info['memory'] * 1024
802 def getResume(self):
803 return "%s" % self.info['resume']
805 def endRestore(self):
806 self.setResume(False)
808 def setResume(self, state):
809 self.info['resume'] = state
811 def refreshShutdown(self, xeninfo = None):
812 # If set at the end of this method, a restart is required, with the
813 # given reason. This restart has to be done out of the scope of
814 # refresh_shutdown_lock.
815 restart_reason = None
817 self.refresh_shutdown_lock.acquire()
818 try:
819 if xeninfo is None:
820 xeninfo = dom_get(self.domid)
821 if xeninfo is None:
822 # The domain no longer exists. This will occur if we have
823 # scheduled a timer to check for shutdown timeouts and the
824 # shutdown succeeded. It will also occur if someone
825 # destroys a domain beneath us. We clean up the domain,
826 # just in case, but we can't clean up the VM, because that
827 # VM may have migrated to a different domain on this
828 # machine.
829 self.cleanupDomain()
830 return
832 if xeninfo['dying']:
833 # Dying means that a domain has been destroyed, but has not
834 # yet been cleaned up by Xen. This state could persist
835 # indefinitely if, for example, another domain has some of its
836 # pages mapped. We might like to diagnose this problem in the
837 # future, but for now all we do is make sure that it's not us
838 # holding the pages, by calling cleanupDomain. We can't
839 # clean up the VM, as above.
840 self.cleanupDomain()
841 return
843 elif xeninfo['crashed']:
844 if self.readDom('xend/shutdown_completed'):
845 # We've seen this shutdown already, but we are preserving
846 # the domain for debugging. Leave it alone.
847 return
849 log.warn('Domain has crashed: name=%s id=%d.',
850 self.info['name'], self.domid)
852 if xroot.get_enable_dump():
853 self.dumpCore()
855 restart_reason = 'crash'
857 elif xeninfo['shutdown']:
858 if self.readDom('xend/shutdown_completed'):
859 # We've seen this shutdown already, but we are preserving
860 # the domain for debugging. Leave it alone.
861 return
863 else:
864 reason = shutdown_reason(xeninfo['shutdown_reason'])
866 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
867 self.info['name'], self.domid, reason)
869 self.clearRestart()
871 if reason == 'suspend':
872 self.state_set(STATE_DOM_SHUTDOWN)
873 # Don't destroy the domain. XendCheckpoint will do
874 # this once it has finished. However, stop watching
875 # the VM path now, otherwise we will end up with one
876 # watch for the old domain, and one for the new.
877 self.unwatchVm()
878 elif reason in ['poweroff', 'reboot']:
879 restart_reason = reason
880 else:
881 self.destroy()
883 elif self.dompath is None:
884 # We have yet to manage to call introduceDomain on this
885 # domain. This can happen if a restore is in progress, or has
886 # failed. Ignore this domain.
887 pass
888 else:
889 # Domain is alive. If we are shutting it down, then check
890 # the timeout on that, and destroy it if necessary.
892 if self.shutdownStartTime:
893 timeout = (SHUTDOWN_TIMEOUT - time.time() +
894 self.shutdownStartTime)
895 if timeout < 0:
896 log.info(
897 "Domain shutdown timeout expired: name=%s id=%s",
898 self.info['name'], self.domid)
899 self.destroy()
900 finally:
901 self.refresh_shutdown_lock.release()
903 if restart_reason:
904 self.maybeRestart(restart_reason)
907 def handleShutdownWatch(self, _):
908 log.debug('XendDomainInfo.handleShutdownWatch')
910 reason = self.readDom('control/shutdown')
912 if reason and reason != 'suspend':
913 sst = self.readDom('xend/shutdown_start_time')
914 now = time.time()
915 if sst:
916 self.shutdownStartTime = float(sst)
917 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
918 else:
919 self.shutdownStartTime = now
920 self.storeDom('xend/shutdown_start_time', now)
921 timeout = SHUTDOWN_TIMEOUT
923 log.trace(
924 "Scheduling refreshShutdown on domain %d in %ds.",
925 self.domid, timeout)
926 threading.Timer(timeout, self.refreshShutdown).start()
928 return True
931 def shutdown(self, reason):
932 if not reason in shutdown_reasons.values():
933 raise XendError('Invalid reason: %s' % reason)
934 self.storeDom("control/shutdown", reason)
937 ## private:
939 def clearRestart(self):
940 self.removeDom("xend/shutdown_start_time")
943 def maybeRestart(self, reason):
944 # Dispatch to the correct method based upon the configured on_{reason}
945 # behaviour.
946 {"destroy" : self.destroy,
947 "restart" : self.restart,
948 "preserve" : self.preserve,
949 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
952 def renameRestart(self):
953 self.restart(True)
956 def dumpCore(self):
957 """Create a core dump for this domain. Nothrow guarantee."""
959 try:
960 corefile = "/var/xen/dump/%s.%s.core" % (self.info['name'],
961 self.domid)
962 xc.domain_dumpcore(self.domid, corefile)
964 except:
965 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
966 self.domid, self.info['name'])
969 ## public:
971 def setMemoryTarget(self, target):
972 """Set the memory target of this domain.
973 @param target In MiB.
974 """
975 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
976 self.info['name'], self.domid, target)
978 self.info['memory'] = target
979 self.storeVm("memory", target)
980 self.storeDom("memory/target", target << 10)
983 def update(self, info = None):
984 """Update with info from xc.domain_getinfo().
985 """
987 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
988 if not info:
989 info = dom_get(self.domid)
990 if not info:
991 return
993 #manually update ssidref / security fields
994 if security.on() and info.has_key('ssidref'):
995 if (info['ssidref'] != 0) and self.info.has_key('security'):
996 security_field = self.info['security']
997 if not security_field:
998 #create new security element
999 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1000 #ssidref field not used any longer
1001 info.pop('ssidref')
1003 self.info.update(info)
1004 self.validateInfo()
1005 self.refreshShutdown(info)
1007 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1008 self.info)
1011 ## private:
1013 def state_set(self, state):
1014 self.state_updated.acquire()
1015 try:
1016 if self.state != state:
1017 self.state = state
1018 self.state_updated.notifyAll()
1019 finally:
1020 self.state_updated.release()
1023 ## public:
1025 def waitForShutdown(self):
1026 self.state_updated.acquire()
1027 try:
1028 while self.state == STATE_DOM_OK:
1029 self.state_updated.wait()
1030 finally:
1031 self.state_updated.release()
1034 def __str__(self):
1035 s = "<domain"
1036 s += " id=" + str(self.domid)
1037 s += " name=" + self.info['name']
1038 s += " memory=" + str(self.info['memory'])
1039 s += ">"
1040 return s
1042 __repr__ = __str__
1045 ## private:
1047 def createDevice(self, deviceClass, devconfig):
1048 return self.getDeviceController(deviceClass).createDevice(devconfig)
1051 def waitForDevices_(self, deviceClass):
1052 return self.getDeviceController(deviceClass).waitForDevices()
1055 def waitForDevice(self, deviceClass, devid):
1056 return self.getDeviceController(deviceClass).waitForDevice(devid)
1059 def reconfigureDevice(self, deviceClass, devid, devconfig):
1060 return self.getDeviceController(deviceClass).reconfigureDevice(
1061 devid, devconfig)
1064 ## public:
1066 def destroyDevice(self, deviceClass, devid):
1067 return self.getDeviceController(deviceClass).destroyDevice(devid)
1070 def getDeviceSxprs(self, deviceClass):
1071 return self.getDeviceController(deviceClass).sxprs()
1074 ## private:
1076 def getDeviceConfigurations(self, deviceClass):
1077 return self.getDeviceController(deviceClass).configurations()
1080 def getDeviceController(self, name):
1081 if name not in controllerClasses:
1082 raise XendError("unknown device type: " + str(name))
1084 return controllerClasses[name](self)
1087 ## public:
1089 def sxpr(self):
1090 sxpr = ['domain',
1091 ['domid', self.domid]]
1093 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1094 if self.infoIsSet(e[0]):
1095 sxpr.append([e[0], self.info[e[0]]])
1097 if self.infoIsSet('image'):
1098 sxpr.append(['image', self.info['image']])
1100 if self.infoIsSet('security'):
1101 sxpr.append(['security', self.info['security']])
1103 for cls in controllerClasses:
1104 for config in self.getDeviceConfigurations(cls):
1105 sxpr.append(['device', config])
1107 def stateChar(name):
1108 if name in self.info:
1109 if self.info[name]:
1110 return name[0]
1111 else:
1112 return '-'
1113 else:
1114 return '?'
1116 state = reduce(
1117 lambda x, y: x + y,
1118 map(stateChar,
1119 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1120 'dying']))
1122 sxpr.append(['state', state])
1123 if self.infoIsSet('shutdown'):
1124 reason = shutdown_reason(self.info['shutdown_reason'])
1125 sxpr.append(['shutdown_reason', reason])
1126 if self.infoIsSet('cpu_time'):
1127 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1128 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1130 if self.infoIsSet('start_time'):
1131 up_time = time.time() - self.info['start_time']
1132 sxpr.append(['up_time', str(up_time) ])
1133 sxpr.append(['start_time', str(self.info['start_time']) ])
1135 if self.store_mfn:
1136 sxpr.append(['store_mfn', self.store_mfn])
1137 if self.console_mfn:
1138 sxpr.append(['console_mfn', self.console_mfn])
1140 return sxpr
1143 def getVCPUInfo(self):
1144 try:
1145 # We include the domain name and ID, to help xm.
1146 sxpr = ['domain',
1147 ['domid', self.domid],
1148 ['name', self.info['name']],
1149 ['vcpu_count', self.info['online_vcpus']]]
1151 for i in range(0, self.info['max_vcpu_id']+1):
1152 info = xc.vcpu_getinfo(self.domid, i)
1154 sxpr.append(['vcpu',
1155 ['number', i],
1156 ['online', info['online']],
1157 ['blocked', info['blocked']],
1158 ['running', info['running']],
1159 ['cpu_time', info['cpu_time'] / 1e9],
1160 ['cpu', info['cpu']],
1161 ['cpumap', info['cpumap']]])
1163 return sxpr
1165 except RuntimeError, exn:
1166 raise XendError(str(exn))
1169 ## private:
1171 def check_name(self, name):
1172 """Check if a vm name is valid. Valid names contain alphabetic characters,
1173 digits, or characters in '_-.:/+'.
1174 The same name cannot be used for more than one vm at the same time.
1176 @param name: name
1177 @raise: VmError if invalid
1178 """
1179 if name is None or name == '':
1180 raise VmError('missing vm name')
1181 for c in name:
1182 if c in string.digits: continue
1183 if c in '_-.:/+': continue
1184 if c in string.ascii_letters: continue
1185 raise VmError('invalid vm name')
1187 dominfo = domain_by_name(name)
1188 if not dominfo:
1189 return
1190 if self.domid is None:
1191 raise VmError("VM name '%s' already in use by domain %d" %
1192 (name, dominfo.domid))
1193 if dominfo.domid != self.domid:
1194 raise VmError("VM name '%s' is used in both domains %d and %d" %
1195 (name, self.domid, dominfo.domid))
1198 def construct(self):
1199 """Construct the domain.
1201 @raise: VmError on error
1202 """
1204 log.debug('XendDomainInfo.construct: %s',
1205 self.domid)
1207 self.domid = xc.domain_create(
1208 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1209 handle = uuid.fromString(self.info['uuid']))
1211 if self.domid < 0:
1212 raise VmError('Creating domain failed: name=%s' %
1213 self.info['name'])
1215 self.dompath = GetDomainPath(self.domid)
1217 self.recreateDom()
1219 # Set maximum number of vcpus in domain
1220 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1223 def introduceDomain(self):
1224 assert self.domid is not None
1225 assert self.store_mfn is not None
1226 assert self.store_port is not None
1228 try:
1229 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1230 except RuntimeError, exn:
1231 raise XendError(str(exn))
1234 def initDomain(self):
1235 log.debug('XendDomainInfo.initDomain: %s %s',
1236 self.domid,
1237 self.info['cpu_weight'])
1239 # if we have a boot loader but no image, then we need to set things
1240 # up by running the boot loader non-interactively
1241 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1242 self.configure_bootloader()
1244 if not self.infoIsSet('image'):
1245 raise VmError('Missing image in configuration')
1247 try:
1248 self.image = image.create(self,
1249 self.info['image'],
1250 self.info['device'])
1252 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1254 # repin domain vcpus if a restricted cpus list is provided
1255 # this is done prior to memory allocation to aide in memory
1256 # distribution for NUMA systems.
1257 cpus = self.info['cpus']
1258 if cpus is not None and len(cpus) > 0:
1259 for v in range(0, self.info['max_vcpu_id']+1):
1260 # pincpu takes a list of ints
1261 cpu = [ int( cpus[v % len(cpus)] ) ]
1262 xc.vcpu_setaffinity(self.domid, v, cpu)
1264 m = self.image.getDomainMemory(self.info['memory'] * 1024)
1265 balloon.free(m)
1266 xc.domain_setmaxmem(self.domid, m)
1267 xc.domain_memory_increase_reservation(self.domid, self.info['memory'] * 1024, 0, 0)
1269 self.createChannels()
1271 channel_details = self.image.createImage()
1273 self.store_mfn = channel_details['store_mfn']
1274 if 'console_mfn' in channel_details:
1275 self.console_mfn = channel_details['console_mfn']
1277 self.introduceDomain()
1279 self.createDevices()
1281 if self.info['bootloader']:
1282 self.image.cleanupBootloading()
1284 self.info['start_time'] = time.time()
1286 except RuntimeError, exn:
1287 raise VmError(str(exn))
1290 ## public:
1292 def cleanupDomain(self):
1293 """Cleanup domain resources; release devices. Idempotent. Nothrow
1294 guarantee."""
1296 self.refresh_shutdown_lock.acquire()
1297 try:
1298 self.unwatchShutdown()
1300 self.release_devices()
1302 if self.image:
1303 try:
1304 self.image.destroy()
1305 except:
1306 log.exception(
1307 "XendDomainInfo.cleanup: image.destroy() failed.")
1308 self.image = None
1310 try:
1311 self.removeDom()
1312 except:
1313 log.exception("Removing domain path failed.")
1315 try:
1316 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1317 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1318 except:
1319 log.exception("Renaming Zombie failed.")
1321 self.state_set(STATE_DOM_SHUTDOWN)
1322 finally:
1323 self.refresh_shutdown_lock.release()
1326 def cleanupVm(self):
1327 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1329 self.unwatchVm()
1331 try:
1332 self.removeVm()
1333 except:
1334 log.exception("Removing VM path failed.")
1337 ## private:
1339 def unwatchVm(self):
1340 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1341 guarantee."""
1343 try:
1344 try:
1345 if self.vmWatch:
1346 self.vmWatch.unwatch()
1347 finally:
1348 self.vmWatch = None
1349 except:
1350 log.exception("Unwatching VM path failed.")
1353 def unwatchShutdown(self):
1354 """Remove the watch on the domain's control/shutdown node, if any.
1355 Idempotent. Nothrow guarantee. Expects to be protected by the
1356 refresh_shutdown_lock."""
1358 try:
1359 try:
1360 if self.shutdownWatch:
1361 self.shutdownWatch.unwatch()
1362 finally:
1363 self.shutdownWatch = None
1364 except:
1365 log.exception("Unwatching control/shutdown failed.")
1368 ## public:
1370 def destroy(self):
1371 """Cleanup VM and destroy domain. Nothrow guarantee."""
1373 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1375 self.cleanupVm()
1376 if self.dompath is not None:
1377 self.destroyDomain()
1380 def destroyDomain(self):
1381 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1383 try:
1384 if self.domid is not None:
1385 xc.domain_destroy(self.domid)
1386 except:
1387 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1389 self.cleanupDomain()
1392 ## private:
1394 def release_devices(self):
1395 """Release all domain's devices. Nothrow guarantee."""
1397 while True:
1398 t = xstransact("%s/device" % self.dompath)
1399 for n in controllerClasses.keys():
1400 for d in t.list(n):
1401 try:
1402 t.remove(d)
1403 except:
1404 # Log and swallow any exceptions in removal --
1405 # there's nothing more we can do.
1406 log.exception(
1407 "Device release failed: %s; %s; %s",
1408 self.info['name'], n, d)
1409 if t.commit():
1410 break
1413 def createChannels(self):
1414 """Create the channels to the domain.
1415 """
1416 self.store_port = self.createChannel()
1417 self.console_port = self.createChannel()
1420 def createChannel(self):
1421 """Create an event channel to the domain.
1422 """
1423 try:
1424 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1425 except:
1426 log.exception("Exception in alloc_unbound(%d)", self.domid)
1427 raise
1430 ## public:
1432 def createDevices(self):
1433 """Create the devices for a vm.
1435 @raise: VmError for invalid devices
1436 """
1438 for (n, c) in self.info['device']:
1439 self.createDevice(n, c)
1441 if self.image:
1442 self.image.createDeviceModel()
1444 ## public:
1446 def testMigrateDevices(self, network, dst):
1447 """ Notify all device about intention of migration
1448 @raise: XendError for a device that cannot be migrated
1449 """
1450 for (n, c) in self.info['device']:
1451 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1452 if rc != 0:
1453 raise XendError("Device of type '%s' refuses migration." % n)
1455 def migrateDevices(self, network, dst, step, domName=''):
1456 """Notify the devices about migration
1457 """
1458 ctr = 0
1459 try:
1460 for (n, c) in self.info['device']:
1461 self.migrateDevice(n, c, network, dst, step, domName)
1462 ctr = ctr + 1
1463 except:
1464 for (n, c) in self.info['device']:
1465 if ctr == 0:
1466 step = step - 1
1467 ctr = ctr - 1
1468 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1469 raise
1471 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1472 step, domName=''):
1473 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1474 network, dst, step, domName)
1476 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1477 dst, step, domName=''):
1478 return self.getDeviceController(deviceClass).recover_migrate(
1479 deviceConfig, network, dst, step, domName)
1481 def waitForDevices(self):
1482 """Wait for this domain's configured devices to connect.
1484 @raise: VmError if any device fails to initialise.
1485 """
1486 for c in controllerClasses:
1487 self.waitForDevices_(c)
1490 def device_create(self, dev_config):
1491 """Create a new device.
1493 @param dev_config: device configuration
1494 """
1495 dev_type = sxp.name(dev_config)
1496 devid = self.createDevice(dev_type, dev_config)
1497 self.waitForDevice(dev_type, devid)
1498 self.info['device'].append((dev_type, dev_config))
1499 return self.getDeviceController(dev_type).sxpr(devid)
1502 def device_configure(self, dev_config, devid):
1503 """Configure an existing device.
1504 @param dev_config: device configuration
1505 @param devid: device id
1506 """
1507 deviceClass = sxp.name(dev_config)
1508 self.reconfigureDevice(deviceClass, devid, dev_config)
1511 def pause(self):
1512 xc.domain_pause(self.domid)
1515 def unpause(self):
1516 xc.domain_unpause(self.domid)
1519 ## private:
1521 def restart(self, rename = False):
1522 """Restart the domain after it has exited.
1524 @param rename True if the old domain is to be renamed and preserved,
1525 False if it is to be destroyed.
1526 """
1528 self.configure_bootloader()
1529 config = self.sxpr()
1531 if self.readVm(RESTART_IN_PROGRESS):
1532 log.error('Xend failed during restart of domain %d. '
1533 'Refusing to restart to avoid loops.',
1534 self.domid)
1535 self.destroy()
1536 return
1538 self.writeVm(RESTART_IN_PROGRESS, 'True')
1540 now = time.time()
1541 rst = self.readVm('xend/previous_restart_time')
1542 if rst:
1543 rst = float(rst)
1544 timeout = now - rst
1545 if timeout < MINIMUM_RESTART_TIME:
1546 log.error(
1547 'VM %s restarting too fast (%f seconds since the last '
1548 'restart). Refusing to restart to avoid loops.',
1549 self.info['name'], timeout)
1550 self.destroy()
1551 return
1553 self.writeVm('xend/previous_restart_time', str(now))
1555 try:
1556 if rename:
1557 self.preserveForRestart()
1558 else:
1559 self.unwatchVm()
1560 self.destroyDomain()
1562 # new_dom's VM will be the same as this domain's VM, except where
1563 # the rename flag has instructed us to call preserveForRestart.
1564 # In that case, it is important that we remove the
1565 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1566 # once the new one is available.
1568 new_dom = None
1569 try:
1570 new_dom = XendDomain.instance().domain_create(config)
1571 new_dom.unpause()
1572 new_dom.removeVm(RESTART_IN_PROGRESS)
1573 except:
1574 if new_dom:
1575 new_dom.removeVm(RESTART_IN_PROGRESS)
1576 new_dom.destroy()
1577 else:
1578 self.removeVm(RESTART_IN_PROGRESS)
1579 raise
1580 except:
1581 log.exception('Failed to restart domain %d.', self.domid)
1584 def preserveForRestart(self):
1585 """Preserve a domain that has been shut down, by giving it a new UUID,
1586 cloning the VM details, and giving it a new name. This allows us to
1587 keep this domain for debugging, but restart a new one in its place
1588 preserving the restart semantics (name and UUID preserved).
1589 """
1591 new_name = self.generateUniqueName()
1592 new_uuid = uuid.toString(uuid.create())
1593 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1594 self.info['name'], self.domid, self.info['uuid'],
1595 new_name, new_uuid)
1596 self.unwatchVm()
1597 self.release_devices()
1598 self.info['name'] = new_name
1599 self.info['uuid'] = new_uuid
1600 self.vmpath = XendDomain.VMROOT + new_uuid
1601 self.storeVmDetails()
1602 self.preserve()
1605 def preserve(self):
1606 log.info("Preserving dead domain %s (%d).", self.info['name'],
1607 self.domid)
1608 self.unwatchVm()
1609 self.storeDom('xend/shutdown_completed', 'True')
1610 self.state_set(STATE_DOM_SHUTDOWN)
1613 # private:
1615 def generateUniqueName(self):
1616 n = 1
1617 while True:
1618 name = "%s-%d" % (self.info['name'], n)
1619 try:
1620 self.check_name(name)
1621 return name
1622 except VmError:
1623 n += 1
1626 def configure_bootloader(self):
1627 """Run the bootloader if we're configured to do so."""
1628 if not self.info['bootloader']:
1629 return
1630 blcfg = None
1631 # FIXME: this assumes that we want to use the first disk device
1632 for (n,c) in self.info['device']:
1633 if not n or not c or n != "vbd":
1634 continue
1635 disk = sxp.child_value(c, "uname")
1636 if disk is None:
1637 continue
1638 fn = blkdev_uname_to_file(disk)
1639 blcfg = bootloader(self.info['bootloader'], fn, 1,
1640 self.info['bootloader_args'],
1641 self.info['image'])
1642 break
1643 if blcfg is None:
1644 msg = "Had a bootloader specified, but can't find disk"
1645 log.error(msg)
1646 raise VmError(msg)
1647 self.info['image'] = blcfg
1650 def send_sysrq(self, key):
1651 asserts.isCharConvertible(key)
1653 self.storeDom("control/sysrq", '%c' % key)
1656 def infoIsSet(self, name):
1657 return name in self.info and self.info[name] is not None
1660 #============================================================================
1661 # Register device controllers and their device config types.
1663 """A map from device-class names to the subclass of DevController that
1664 implements the device control specific to that device-class."""
1665 controllerClasses = {}
1667 def addControllerClass(device_class, cls):
1668 """Register a subclass of DevController to handle the named device-class.
1669 """
1670 cls.deviceClass = device_class
1671 controllerClasses[device_class] = cls
1674 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1675 addControllerClass('vbd', blkif.BlkifController)
1676 addControllerClass('vif', netif.NetifController)
1677 addControllerClass('vtpm', tpmif.TPMifController)
1678 addControllerClass('pci', pciif.PciController)
1679 addControllerClass('ioports', iopif.IOPortsController)
1680 addControllerClass('irq', irqif.IRQController)
1681 addControllerClass('usb', usbif.UsbifController)