ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 9885:ef6216b43278

Instead of relying on xm create to always run the bootloader, make sure
we run it if we get into domain creation with a bootloader set but no
image. This could happen if someone creates a domain config via the
XML-RPC or sxp interfaces.

Signed-off-by: Jeremy Katz <katzj@redhat.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Apr 28 14:11:57 2006 +0100 (2006-04-28)
parents 268e45409ecd
children 40cd49c88d69
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 ('features', str),
136 ]
138 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
141 ##
142 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
143 # entries written to the store that cannot be reconfigured on-the-fly.
144 #
145 VM_STORE_ENTRIES = [
146 ('uuid', str),
147 ('vcpus', int),
148 ('vcpu_avail', int),
149 ('memory', int),
150 ('maxmem', int),
151 ('start_time', float),
152 ]
154 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
157 #
158 # There are a number of CPU-related fields:
159 #
160 # vcpus: the number of virtual CPUs this domain is configured to use.
161 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
162 # its VCPUs. This is translated to
163 # <dompath>/cpu/<id>/availability = {online,offline} for use
164 # by the guest domain.
165 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
166 # CPUs that that VCPU may use.
167 # cpu: a configuration setting requesting that VCPU 0 is pinned to
168 # the specified physical CPU.
169 #
170 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
171 # across save, restore, migrate, and restart). The other settings are only
172 # specific to the domain, so are lost when the VM moves.
173 #
176 def create(config):
177 """Create a VM from a configuration.
179 @param config configuration
180 @raise: VmError for invalid configuration
181 """
183 log.debug("XendDomainInfo.create(%s)", config)
185 vm = XendDomainInfo(parseConfig(config))
186 try:
187 vm.construct()
188 vm.initDomain()
189 vm.storeVmDetails()
190 vm.storeDomDetails()
191 vm.registerWatches()
192 vm.refreshShutdown()
193 return vm
194 except:
195 log.exception('Domain construction failed')
196 vm.destroy()
197 raise
200 def recreate(xeninfo, priv):
201 """Create the VM object for an existing domain. The domain must not
202 be dying, as the paths in the store should already have been removed,
203 and asking us to recreate them causes problems."""
205 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
207 assert not xeninfo['dying']
209 domid = xeninfo['dom']
210 uuid1 = xeninfo['handle']
211 xeninfo['uuid'] = uuid.toString(uuid1)
212 dompath = GetDomainPath(domid)
213 if not dompath:
214 raise XendError(
215 'No domain path in store for existing domain %d' % domid)
217 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
218 try:
219 vmpath = xstransact.Read(dompath, "vm")
220 if not vmpath:
221 raise XendError(
222 'No vm path in store for existing domain %d' % domid)
223 uuid2_str = xstransact.Read(vmpath, "uuid")
224 if not uuid2_str:
225 raise XendError(
226 'No vm/uuid path in store for existing domain %d' % domid)
228 uuid2 = uuid.fromString(uuid2_str)
230 if uuid1 != uuid2:
231 raise XendError(
232 'Uuid in store does not match uuid for existing domain %d: '
233 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
235 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
237 except Exception, exn:
238 if priv:
239 log.warn(str(exn))
241 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
242 vm.recreateDom()
243 vm.removeVm()
244 vm.storeVmDetails()
245 vm.storeDomDetails()
247 vm.registerWatches()
248 vm.refreshShutdown(xeninfo)
249 return vm
252 def restore(config):
253 """Create a domain and a VM object to do a restore.
255 @param config: domain configuration
256 """
258 log.debug("XendDomainInfo.restore(%s)", config)
260 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
261 try:
262 vm.construct()
263 vm.storeVmDetails()
264 vm.createDevices()
265 vm.createChannels()
266 vm.storeDomDetails()
267 vm.endRestore()
268 return vm
269 except:
270 vm.destroy()
271 raise
274 def parseConfig(config):
275 def get_cfg(name, conv = None):
276 val = sxp.child_value(config, name)
278 if conv and not val is None:
279 try:
280 return conv(val)
281 except TypeError, exn:
282 raise VmError(
283 'Invalid setting %s = %s in configuration: %s' %
284 (name, val, str(exn)))
285 else:
286 return val
289 log.debug("parseConfig: config is %s", config)
291 result = {}
293 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
294 result[e[0]] = get_cfg(e[0], e[1])
296 result['cpu'] = get_cfg('cpu', int)
297 result['cpus'] = get_cfg('cpus', str)
298 result['image'] = get_cfg('image')
299 tmp_security = get_cfg('security')
300 if tmp_security:
301 result['security'] = tmp_security
303 try:
304 if result['image']:
305 v = sxp.child_value(result['image'], 'vcpus')
306 if result['vcpus'] is None and v is not None:
307 result['vcpus'] = int(v)
308 elif v is not None and int(v) != result['vcpus']:
309 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
310 ' Using %s VCPUs for VM %s.') %
311 (result['vcpus'], v, result['uuid']))
312 result['vcpus'] = int(v)
313 except TypeError, exn:
314 raise VmError(
315 'Invalid configuration setting: vcpus = %s: %s' %
316 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
318 try:
319 # support legacy config files with 'cpu' parameter
320 # NB: prepending to list to support previous behavior
321 # where 'cpu' parameter pinned VCPU0.
322 if result['cpu']:
323 if result['cpus']:
324 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
325 else:
326 result['cpus'] = str(result['cpu'])
328 # convert 'cpus' string to list of ints
329 # 'cpus' supports a list of ranges (0-3), seperated by
330 # commas, and negation, (^1).
331 # Precedence is settled by order of the string:
332 # "0-3,^1" -> [0,2,3]
333 # "0-3,^1,1" -> [0,1,2,3]
334 if result['cpus']:
335 cpus = []
336 for c in result['cpus'].split(','):
337 if c.find('-') != -1:
338 (x,y) = c.split('-')
339 for i in range(int(x),int(y)+1):
340 cpus.append(int(i))
341 else:
342 # remove this element from the list
343 if c[0] == '^':
344 cpus = [x for x in cpus if x != int(c[1:])]
345 else:
346 cpus.append(int(c))
348 result['cpus'] = cpus
350 except ValueError, exn:
351 raise VmError(
352 'Invalid configuration setting: cpus = %s: %s' %
353 (result['cpus'], exn))
355 result['backend'] = []
356 for c in sxp.children(config, 'backend'):
357 result['backend'].append(sxp.name(sxp.child0(c)))
359 result['device'] = []
360 for d in sxp.children(config, 'device'):
361 c = sxp.child0(d)
362 result['device'].append((sxp.name(c), c))
364 # Configuration option "restart" is deprecated. Parse it, but
365 # let on_xyz override it if they are present.
366 restart = get_cfg('restart')
367 if restart:
368 def handle_restart(event, val):
369 if result[event] is None:
370 result[event] = val
372 if restart == "onreboot":
373 handle_restart('on_poweroff', 'destroy')
374 handle_restart('on_reboot', 'restart')
375 handle_restart('on_crash', 'destroy')
376 elif restart == "always":
377 handle_restart('on_poweroff', 'restart')
378 handle_restart('on_reboot', 'restart')
379 handle_restart('on_crash', 'restart')
380 elif restart == "never":
381 handle_restart('on_poweroff', 'destroy')
382 handle_restart('on_reboot', 'destroy')
383 handle_restart('on_crash', 'destroy')
384 else:
385 log.warn("Ignoring malformed and deprecated config option "
386 "restart = %s", restart)
388 log.debug("parseConfig: result is %s", result)
389 return result
392 def domain_by_name(name):
393 return XendDomain.instance().domain_lookup_by_name_nr(name)
396 def shutdown_reason(code):
397 """Get a shutdown reason from a code.
399 @param code: shutdown code
400 @type code: int
401 @return: shutdown reason
402 @rtype: string
403 """
404 return shutdown_reasons.get(code, "?")
406 def dom_get(dom):
407 """Get info from xen for an existing domain.
409 @param dom: domain id
410 @return: info or None
411 """
412 try:
413 domlist = xc.domain_getinfo(dom, 1)
414 if domlist and dom == domlist[0]['dom']:
415 return domlist[0]
416 except Exception, err:
417 # ignore missing domain
418 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
419 return None
422 class XendDomainInfo:
424 def __init__(self, info, domid = None, dompath = None, augment = False,
425 priv = False, resume = False):
427 self.info = info
429 if not self.infoIsSet('uuid'):
430 self.info['uuid'] = uuid.toString(uuid.create())
432 if domid is not None:
433 self.domid = domid
434 elif 'dom' in info:
435 self.domid = int(info['dom'])
436 else:
437 self.domid = None
439 self.vmpath = XendDomain.VMROOT + self.info['uuid']
440 self.dompath = dompath
442 if augment:
443 self.augmentInfo(priv)
445 self.validateInfo()
447 self.image = None
448 self.security = None
449 self.store_port = None
450 self.store_mfn = None
451 self.console_port = None
452 self.console_mfn = None
454 self.vmWatch = None
455 self.shutdownWatch = None
457 self.shutdownStartTime = None
459 self.state = STATE_DOM_OK
460 self.state_updated = threading.Condition()
461 self.refresh_shutdown_lock = threading.Condition()
463 self.setResume(resume)
465 ## private:
467 def readVMDetails(self, params):
468 """Read the specified parameters from the store.
469 """
470 try:
471 return self.gatherVm(*params)
472 except ValueError:
473 # One of the int/float entries in params has a corresponding store
474 # entry that is invalid. We recover, because older versions of
475 # Xend may have put the entry there (memory/target, for example),
476 # but this is in general a bad situation to have reached.
477 log.exception(
478 "Store corrupted at %s! Domain %d's configuration may be "
479 "affected.", self.vmpath, self.domid)
480 return []
483 def storeChanged(self, _):
484 log.trace("XendDomainInfo.storeChanged");
486 changed = False
488 def f(x, y):
489 if y is not None and self.info[x[0]] != y:
490 self.info[x[0]] = y
491 changed = True
493 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
495 im = self.readVm('image')
496 current_im = self.info['image']
497 if (im is not None and
498 (current_im is None or sxp.to_string(current_im) != im)):
499 self.info['image'] = sxp.from_string(im)
500 changed = True
502 if changed:
503 # Update the domain section of the store, as this contains some
504 # parameters derived from the VM configuration.
505 self.storeDomDetails()
507 return 1
510 def augmentInfo(self, priv):
511 """Augment self.info, as given to us through {@link #recreate}, with
512 values taken from the store. This recovers those values known to xend
513 but not to the hypervisor.
514 """
515 def useIfNeeded(name, val):
516 if not self.infoIsSet(name) and val is not None:
517 self.info[name] = val
519 if priv:
520 entries = VM_STORE_ENTRIES[:]
521 entries.remove(('memory', int))
522 entries.remove(('maxmem', int))
523 else:
524 entries = VM_STORE_ENTRIES
525 entries.append(('image', str))
526 entries.append(('security', str))
528 map(lambda x, y: useIfNeeded(x[0], y), entries,
529 self.readVMDetails(entries))
531 device = []
532 for c in controllerClasses:
533 devconfig = self.getDeviceConfigurations(c)
534 if devconfig:
535 device.extend(map(lambda x: (c, x), devconfig))
536 useIfNeeded('device', device)
539 def validateInfo(self):
540 """Validate and normalise the info block. This has either been parsed
541 by parseConfig, or received from xc through recreate and augmented by
542 the current store contents.
543 """
544 def defaultInfo(name, val):
545 if not self.infoIsSet(name):
546 self.info[name] = val()
548 try:
549 defaultInfo('name', lambda: "Domain-%d" % self.domid)
550 defaultInfo('on_poweroff', lambda: "destroy")
551 defaultInfo('on_reboot', lambda: "restart")
552 defaultInfo('on_crash', lambda: "restart")
553 defaultInfo('features', lambda: "")
554 defaultInfo('cpu', lambda: None)
555 defaultInfo('cpus', lambda: [])
556 defaultInfo('cpu_weight', lambda: 1.0)
558 # some domains don't have a config file (e.g. dom0 )
559 # to set number of vcpus so we derive available cpus
560 # from max_vcpu_id which is present for running domains.
561 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
562 avail = int(self.info['max_vcpu_id'])+1
563 else:
564 avail = int(1)
566 defaultInfo('vcpus', lambda: avail)
567 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
568 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
569 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
571 defaultInfo('memory', lambda: 0)
572 defaultInfo('maxmem', lambda: 0)
573 defaultInfo('bootloader', lambda: None)
574 defaultInfo('backend', lambda: [])
575 defaultInfo('device', lambda: [])
576 defaultInfo('image', lambda: None)
577 defaultInfo('security', lambda: None)
579 self.check_name(self.info['name'])
581 if isinstance(self.info['image'], str):
582 self.info['image'] = sxp.from_string(self.info['image'])
584 if isinstance(self.info['security'], str):
585 self.info['security'] = sxp.from_string(self.info['security'])
587 if self.info['memory'] == 0:
588 if self.infoIsSet('mem_kb'):
589 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
591 if self.info['maxmem'] < self.info['memory']:
592 self.info['maxmem'] = self.info['memory']
594 for (n, c) in self.info['device']:
595 if not n or not c or n not in controllerClasses:
596 raise VmError('invalid device (%s, %s)' %
597 (str(n), str(c)))
599 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
600 if self.info[event] not in restart_modes:
601 raise VmError('invalid restart event: %s = %s' %
602 (event, str(self.info[event])))
604 except KeyError, exn:
605 log.exception(exn)
606 raise VmError('Unspecified domain detail: %s' % exn)
609 def readVm(self, *args):
610 return xstransact.Read(self.vmpath, *args)
612 def writeVm(self, *args):
613 return xstransact.Write(self.vmpath, *args)
615 def removeVm(self, *args):
616 return xstransact.Remove(self.vmpath, *args)
618 def gatherVm(self, *args):
619 return xstransact.Gather(self.vmpath, *args)
622 ## public:
624 def storeVm(self, *args):
625 return xstransact.Store(self.vmpath, *args)
628 ## private:
630 def readDom(self, *args):
631 return xstransact.Read(self.dompath, *args)
633 def writeDom(self, *args):
634 return xstransact.Write(self.dompath, *args)
637 ## public:
639 def removeDom(self, *args):
640 return xstransact.Remove(self.dompath, *args)
642 def recreateDom(self):
643 complete(self.dompath, lambda t: self._recreateDom(t))
645 def _recreateDom(self, t):
646 t.remove()
647 t.mkdir()
648 t.set_permissions({ 'dom' : self.domid })
651 ## private:
653 def storeDom(self, *args):
654 return xstransact.Store(self.dompath, *args)
657 ## public:
659 def completeRestore(self, store_mfn, console_mfn):
661 log.debug("XendDomainInfo.completeRestore")
663 self.store_mfn = store_mfn
664 self.console_mfn = console_mfn
666 self.introduceDomain()
667 self.storeDomDetails()
668 self.registerWatches()
669 self.refreshShutdown()
671 log.debug("XendDomainInfo.completeRestore done")
674 def storeVmDetails(self):
675 to_store = {}
677 for k in VM_STORE_ENTRIES:
678 if self.infoIsSet(k[0]):
679 to_store[k[0]] = str(self.info[k[0]])
681 if self.infoIsSet('image'):
682 to_store['image'] = sxp.to_string(self.info['image'])
684 if self.infoIsSet('security'):
685 security = self.info['security']
686 to_store['security'] = sxp.to_string(security)
687 for idx in range(0, len(security)):
688 if security[idx][0] == 'access_control':
689 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
690 for aidx in range(1, len(security[idx])):
691 if security[idx][aidx][0] == 'label':
692 to_store['security/access_control/label'] = security[idx][aidx][1]
693 if security[idx][aidx][0] == 'policy':
694 to_store['security/access_control/policy'] = security[idx][aidx][1]
695 if security[idx][0] == 'ssidref':
696 to_store['security/ssidref'] = str(security[idx][1])
698 log.debug("Storing VM details: %s", to_store)
700 self.writeVm(to_store)
703 def storeDomDetails(self):
704 to_store = {
705 'domid': str(self.domid),
706 'vm': self.vmpath,
707 'name': self.info['name'],
708 'console/limit': str(xroot.get_console_limit() * 1024),
709 'memory/target': str(self.info['memory'] * 1024)
710 }
712 def f(n, v):
713 if v is not None:
714 to_store[n] = str(v)
716 f('console/port', self.console_port)
717 f('console/ring-ref', self.console_mfn)
718 f('store/port', self.store_port)
719 f('store/ring-ref', self.store_mfn)
721 to_store.update(self.vcpuDomDetails())
723 log.debug("Storing domain details: %s", to_store)
725 self.writeDom(to_store)
728 ## private:
730 def vcpuDomDetails(self):
731 def availability(n):
732 if self.info['vcpu_avail'] & (1 << n):
733 return 'online'
734 else:
735 return 'offline'
737 result = {}
738 for v in range(0, self.info['vcpus']):
739 result["cpu/%d/availability" % v] = availability(v)
740 return result
743 ## public:
745 def registerWatches(self):
746 """Register a watch on this VM's entries in the store, and the
747 domain's control/shutdown node, so that when they are changed
748 externally, we keep up to date. This should only be called by {@link
749 #create}, {@link #recreate}, or {@link #restore}, once the domain's
750 details have been written, but before the new instance is returned."""
751 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
752 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
753 self.handleShutdownWatch)
756 def getDomid(self):
757 return self.domid
759 def setName(self, name):
760 self.check_name(name)
761 self.info['name'] = name
762 self.storeVm("name", name)
764 def getName(self):
765 return self.info['name']
767 def getDomainPath(self):
768 return self.dompath
771 def getStorePort(self):
772 """For use only by image.py and XendCheckpoint.py."""
773 return self.store_port
776 def getConsolePort(self):
777 """For use only by image.py and XendCheckpoint.py"""
778 return self.console_port
780 def getFeatures(self):
781 """For use only by image.py."""
782 return self.info['features']
784 def getVCpuCount(self):
785 return self.info['vcpus']
788 def setVCpuCount(self, vcpus):
789 self.info['vcpu_avail'] = (1 << vcpus) - 1
790 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
791 self.writeDom(self.vcpuDomDetails())
793 def getLabel(self):
794 return security.get_security_info(self.info, 'label')
796 def getMemoryTarget(self):
797 """Get this domain's target memory size, in KB."""
798 return self.info['memory'] * 1024
800 def getResume(self):
801 return "%s" % self.info['resume']
803 def endRestore(self):
804 self.setResume(False)
806 def setResume(self, state):
807 self.info['resume'] = state
809 def refreshShutdown(self, xeninfo = None):
810 # If set at the end of this method, a restart is required, with the
811 # given reason. This restart has to be done out of the scope of
812 # refresh_shutdown_lock.
813 restart_reason = None
815 self.refresh_shutdown_lock.acquire()
816 try:
817 if xeninfo is None:
818 xeninfo = dom_get(self.domid)
819 if xeninfo is None:
820 # The domain no longer exists. This will occur if we have
821 # scheduled a timer to check for shutdown timeouts and the
822 # shutdown succeeded. It will also occur if someone
823 # destroys a domain beneath us. We clean up the domain,
824 # just in case, but we can't clean up the VM, because that
825 # VM may have migrated to a different domain on this
826 # machine.
827 self.cleanupDomain()
828 return
830 if xeninfo['dying']:
831 # Dying means that a domain has been destroyed, but has not
832 # yet been cleaned up by Xen. This state could persist
833 # indefinitely if, for example, another domain has some of its
834 # pages mapped. We might like to diagnose this problem in the
835 # future, but for now all we do is make sure that it's not us
836 # holding the pages, by calling cleanupDomain. We can't
837 # clean up the VM, as above.
838 self.cleanupDomain()
839 return
841 elif xeninfo['crashed']:
842 if self.readDom('xend/shutdown_completed'):
843 # We've seen this shutdown already, but we are preserving
844 # the domain for debugging. Leave it alone.
845 return
847 log.warn('Domain has crashed: name=%s id=%d.',
848 self.info['name'], self.domid)
850 if xroot.get_enable_dump():
851 self.dumpCore()
853 restart_reason = 'crash'
855 elif xeninfo['shutdown']:
856 if self.readDom('xend/shutdown_completed'):
857 # We've seen this shutdown already, but we are preserving
858 # the domain for debugging. Leave it alone.
859 return
861 else:
862 reason = shutdown_reason(xeninfo['shutdown_reason'])
864 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
865 self.info['name'], self.domid, reason)
867 self.clearRestart()
869 if reason == 'suspend':
870 self.state_set(STATE_DOM_SHUTDOWN)
871 # Don't destroy the domain. XendCheckpoint will do
872 # this once it has finished. However, stop watching
873 # the VM path now, otherwise we will end up with one
874 # watch for the old domain, and one for the new.
875 self.unwatchVm()
876 elif reason in ['poweroff', 'reboot']:
877 restart_reason = reason
878 else:
879 self.destroy()
881 elif self.dompath is None:
882 # We have yet to manage to call introduceDomain on this
883 # domain. This can happen if a restore is in progress, or has
884 # failed. Ignore this domain.
885 pass
886 else:
887 # Domain is alive. If we are shutting it down, then check
888 # the timeout on that, and destroy it if necessary.
890 if self.shutdownStartTime:
891 timeout = (SHUTDOWN_TIMEOUT - time.time() +
892 self.shutdownStartTime)
893 if timeout < 0:
894 log.info(
895 "Domain shutdown timeout expired: name=%s id=%s",
896 self.info['name'], self.domid)
897 self.destroy()
898 finally:
899 self.refresh_shutdown_lock.release()
901 if restart_reason:
902 self.maybeRestart(restart_reason)
905 def handleShutdownWatch(self, _):
906 log.debug('XendDomainInfo.handleShutdownWatch')
908 reason = self.readDom('control/shutdown')
910 if reason and reason != 'suspend':
911 sst = self.readDom('xend/shutdown_start_time')
912 now = time.time()
913 if sst:
914 self.shutdownStartTime = float(sst)
915 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
916 else:
917 self.shutdownStartTime = now
918 self.storeDom('xend/shutdown_start_time', now)
919 timeout = SHUTDOWN_TIMEOUT
921 log.trace(
922 "Scheduling refreshShutdown on domain %d in %ds.",
923 self.domid, timeout)
924 threading.Timer(timeout, self.refreshShutdown).start()
926 return True
929 def shutdown(self, reason):
930 if not reason in shutdown_reasons.values():
931 raise XendError('Invalid reason: %s' % reason)
932 self.storeDom("control/shutdown", reason)
935 ## private:
937 def clearRestart(self):
938 self.removeDom("xend/shutdown_start_time")
941 def maybeRestart(self, reason):
942 # Dispatch to the correct method based upon the configured on_{reason}
943 # behaviour.
944 {"destroy" : self.destroy,
945 "restart" : self.restart,
946 "preserve" : self.preserve,
947 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
950 def renameRestart(self):
951 self.restart(True)
954 def dumpCore(self):
955 """Create a core dump for this domain. Nothrow guarantee."""
957 try:
958 corefile = "/var/xen/dump/%s.%s.core" % (self.info['name'],
959 self.domid)
960 xc.domain_dumpcore(self.domid, corefile)
962 except:
963 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
964 self.domid, self.info['name'])
967 ## public:
969 def setMemoryTarget(self, target):
970 """Set the memory target of this domain.
971 @param target In MiB.
972 """
973 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
974 self.info['name'], self.domid, target)
976 self.info['memory'] = target
977 self.storeVm("memory", target)
978 self.storeDom("memory/target", target << 10)
981 def update(self, info = None):
982 """Update with info from xc.domain_getinfo().
983 """
985 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
986 if not info:
987 info = dom_get(self.domid)
988 if not info:
989 return
991 #manually update ssidref / security fields
992 if security.on() and info.has_key('ssidref'):
993 if (info['ssidref'] != 0) and self.info.has_key('security'):
994 security_field = self.info['security']
995 if not security_field:
996 #create new security element
997 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
998 #ssidref field not used any longer
999 info.pop('ssidref')
1001 self.info.update(info)
1002 self.validateInfo()
1003 self.refreshShutdown(info)
1005 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1006 self.info)
1009 ## private:
1011 def state_set(self, state):
1012 self.state_updated.acquire()
1013 try:
1014 if self.state != state:
1015 self.state = state
1016 self.state_updated.notifyAll()
1017 finally:
1018 self.state_updated.release()
1021 ## public:
1023 def waitForShutdown(self):
1024 self.state_updated.acquire()
1025 try:
1026 while self.state == STATE_DOM_OK:
1027 self.state_updated.wait()
1028 finally:
1029 self.state_updated.release()
1032 def __str__(self):
1033 s = "<domain"
1034 s += " id=" + str(self.domid)
1035 s += " name=" + self.info['name']
1036 s += " memory=" + str(self.info['memory'])
1037 s += ">"
1038 return s
1040 __repr__ = __str__
1043 ## private:
1045 def createDevice(self, deviceClass, devconfig):
1046 return self.getDeviceController(deviceClass).createDevice(devconfig)
1049 def waitForDevices_(self, deviceClass):
1050 return self.getDeviceController(deviceClass).waitForDevices()
1053 def waitForDevice(self, deviceClass, devid):
1054 return self.getDeviceController(deviceClass).waitForDevice(devid)
1057 def reconfigureDevice(self, deviceClass, devid, devconfig):
1058 return self.getDeviceController(deviceClass).reconfigureDevice(
1059 devid, devconfig)
1062 ## public:
1064 def destroyDevice(self, deviceClass, devid):
1065 return self.getDeviceController(deviceClass).destroyDevice(devid)
1068 def getDeviceSxprs(self, deviceClass):
1069 return self.getDeviceController(deviceClass).sxprs()
1072 ## private:
1074 def getDeviceConfigurations(self, deviceClass):
1075 return self.getDeviceController(deviceClass).configurations()
1078 def getDeviceController(self, name):
1079 if name not in controllerClasses:
1080 raise XendError("unknown device type: " + str(name))
1082 return controllerClasses[name](self)
1085 ## public:
1087 def sxpr(self):
1088 sxpr = ['domain',
1089 ['domid', self.domid]]
1091 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1092 if self.infoIsSet(e[0]):
1093 sxpr.append([e[0], self.info[e[0]]])
1095 if self.infoIsSet('image'):
1096 sxpr.append(['image', self.info['image']])
1098 if self.infoIsSet('security'):
1099 sxpr.append(['security', self.info['security']])
1101 for cls in controllerClasses:
1102 for config in self.getDeviceConfigurations(cls):
1103 sxpr.append(['device', config])
1105 def stateChar(name):
1106 if name in self.info:
1107 if self.info[name]:
1108 return name[0]
1109 else:
1110 return '-'
1111 else:
1112 return '?'
1114 state = reduce(
1115 lambda x, y: x + y,
1116 map(stateChar,
1117 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1118 'dying']))
1120 sxpr.append(['state', state])
1121 if self.infoIsSet('shutdown'):
1122 reason = shutdown_reason(self.info['shutdown_reason'])
1123 sxpr.append(['shutdown_reason', reason])
1124 if self.infoIsSet('cpu_time'):
1125 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1126 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1128 if self.infoIsSet('start_time'):
1129 up_time = time.time() - self.info['start_time']
1130 sxpr.append(['up_time', str(up_time) ])
1131 sxpr.append(['start_time', str(self.info['start_time']) ])
1133 if self.store_mfn:
1134 sxpr.append(['store_mfn', self.store_mfn])
1135 if self.console_mfn:
1136 sxpr.append(['console_mfn', self.console_mfn])
1138 return sxpr
1141 def getVCPUInfo(self):
1142 try:
1143 # We include the domain name and ID, to help xm.
1144 sxpr = ['domain',
1145 ['domid', self.domid],
1146 ['name', self.info['name']],
1147 ['vcpu_count', self.info['online_vcpus']]]
1149 for i in range(0, self.info['max_vcpu_id']+1):
1150 info = xc.vcpu_getinfo(self.domid, i)
1152 sxpr.append(['vcpu',
1153 ['number', i],
1154 ['online', info['online']],
1155 ['blocked', info['blocked']],
1156 ['running', info['running']],
1157 ['cpu_time', info['cpu_time'] / 1e9],
1158 ['cpu', info['cpu']],
1159 ['cpumap', info['cpumap']]])
1161 return sxpr
1163 except RuntimeError, exn:
1164 raise XendError(str(exn))
1167 ## private:
1169 def check_name(self, name):
1170 """Check if a vm name is valid. Valid names contain alphabetic characters,
1171 digits, or characters in '_-.:/+'.
1172 The same name cannot be used for more than one vm at the same time.
1174 @param name: name
1175 @raise: VmError if invalid
1176 """
1177 if name is None or name == '':
1178 raise VmError('missing vm name')
1179 for c in name:
1180 if c in string.digits: continue
1181 if c in '_-.:/+': continue
1182 if c in string.ascii_letters: continue
1183 raise VmError('invalid vm name')
1185 dominfo = domain_by_name(name)
1186 if not dominfo:
1187 return
1188 if self.domid is None:
1189 raise VmError("VM name '%s' already in use by domain %d" %
1190 (name, dominfo.domid))
1191 if dominfo.domid != self.domid:
1192 raise VmError("VM name '%s' is used in both domains %d and %d" %
1193 (name, self.domid, dominfo.domid))
1196 def construct(self):
1197 """Construct the domain.
1199 @raise: VmError on error
1200 """
1202 log.debug('XendDomainInfo.construct: %s',
1203 self.domid)
1205 self.domid = xc.domain_create(
1206 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1207 handle = uuid.fromString(self.info['uuid']))
1209 if self.domid < 0:
1210 raise VmError('Creating domain failed: name=%s' %
1211 self.info['name'])
1213 self.dompath = GetDomainPath(self.domid)
1215 self.recreateDom()
1217 # Set maximum number of vcpus in domain
1218 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1221 def introduceDomain(self):
1222 assert self.domid is not None
1223 assert self.store_mfn is not None
1224 assert self.store_port is not None
1226 try:
1227 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1228 except RuntimeError, exn:
1229 raise XendError(str(exn))
1232 def initDomain(self):
1233 log.debug('XendDomainInfo.initDomain: %s %s',
1234 self.domid,
1235 self.info['cpu_weight'])
1237 # if we have a boot loader but no image, then we need to set things
1238 # up by running the boot loader non-interactively
1239 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1240 self.configure_bootloader()
1242 if not self.infoIsSet('image'):
1243 raise VmError('Missing image in configuration')
1245 try:
1246 self.image = image.create(self,
1247 self.info['image'],
1248 self.info['device'])
1250 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1252 # repin domain vcpus if a restricted cpus list is provided
1253 # this is done prior to memory allocation to aide in memory
1254 # distribution for NUMA systems.
1255 cpus = self.info['cpus']
1256 if cpus is not None and len(cpus) > 0:
1257 for v in range(0, self.info['max_vcpu_id']+1):
1258 # pincpu takes a list of ints
1259 cpu = [ int( cpus[v % len(cpus)] ) ]
1260 xc.vcpu_setaffinity(self.domid, v, cpu)
1262 m = self.image.getDomainMemory(self.info['memory'] * 1024)
1263 balloon.free(m)
1264 xc.domain_setmaxmem(self.domid, m)
1265 xc.domain_memory_increase_reservation(self.domid, m, 0, 0)
1267 self.createChannels()
1269 channel_details = self.image.createImage()
1271 self.store_mfn = channel_details['store_mfn']
1272 if 'console_mfn' in channel_details:
1273 self.console_mfn = channel_details['console_mfn']
1275 self.introduceDomain()
1277 self.createDevices()
1279 if self.info['bootloader']:
1280 self.image.cleanupBootloading()
1282 self.info['start_time'] = time.time()
1284 except RuntimeError, exn:
1285 raise VmError(str(exn))
1288 ## public:
1290 def cleanupDomain(self):
1291 """Cleanup domain resources; release devices. Idempotent. Nothrow
1292 guarantee."""
1294 self.refresh_shutdown_lock.acquire()
1295 try:
1296 self.unwatchShutdown()
1298 self.release_devices()
1300 if self.image:
1301 try:
1302 self.image.destroy()
1303 except:
1304 log.exception(
1305 "XendDomainInfo.cleanup: image.destroy() failed.")
1306 self.image = None
1308 try:
1309 self.removeDom()
1310 except:
1311 log.exception("Removing domain path failed.")
1313 try:
1314 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1315 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1316 except:
1317 log.exception("Renaming Zombie failed.")
1319 self.state_set(STATE_DOM_SHUTDOWN)
1320 finally:
1321 self.refresh_shutdown_lock.release()
1324 def cleanupVm(self):
1325 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1327 self.unwatchVm()
1329 try:
1330 self.removeVm()
1331 except:
1332 log.exception("Removing VM path failed.")
1335 ## private:
1337 def unwatchVm(self):
1338 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1339 guarantee."""
1341 try:
1342 try:
1343 if self.vmWatch:
1344 self.vmWatch.unwatch()
1345 finally:
1346 self.vmWatch = None
1347 except:
1348 log.exception("Unwatching VM path failed.")
1351 def unwatchShutdown(self):
1352 """Remove the watch on the domain's control/shutdown node, if any.
1353 Idempotent. Nothrow guarantee. Expects to be protected by the
1354 refresh_shutdown_lock."""
1356 try:
1357 try:
1358 if self.shutdownWatch:
1359 self.shutdownWatch.unwatch()
1360 finally:
1361 self.shutdownWatch = None
1362 except:
1363 log.exception("Unwatching control/shutdown failed.")
1366 ## public:
1368 def destroy(self):
1369 """Cleanup VM and destroy domain. Nothrow guarantee."""
1371 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1373 self.cleanupVm()
1374 if self.dompath is not None:
1375 self.destroyDomain()
1378 def destroyDomain(self):
1379 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1381 try:
1382 if self.domid is not None:
1383 xc.domain_destroy(self.domid)
1384 except:
1385 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1387 self.cleanupDomain()
1390 ## private:
1392 def release_devices(self):
1393 """Release all domain's devices. Nothrow guarantee."""
1395 while True:
1396 t = xstransact("%s/device" % self.dompath)
1397 for n in controllerClasses.keys():
1398 for d in t.list(n):
1399 try:
1400 t.remove(d)
1401 except:
1402 # Log and swallow any exceptions in removal --
1403 # there's nothing more we can do.
1404 log.exception(
1405 "Device release failed: %s; %s; %s",
1406 self.info['name'], n, d)
1407 if t.commit():
1408 break
1411 def createChannels(self):
1412 """Create the channels to the domain.
1413 """
1414 self.store_port = self.createChannel()
1415 self.console_port = self.createChannel()
1418 def createChannel(self):
1419 """Create an event channel to the domain.
1420 """
1421 try:
1422 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1423 except:
1424 log.exception("Exception in alloc_unbound(%d)", self.domid)
1425 raise
1428 ## public:
1430 def createDevices(self):
1431 """Create the devices for a vm.
1433 @raise: VmError for invalid devices
1434 """
1436 for (n, c) in self.info['device']:
1437 self.createDevice(n, c)
1439 if self.image:
1440 self.image.createDeviceModel()
1442 ## public:
1444 def testMigrateDevices(self, live, dst):
1445 """ Notify all device about intention of migration
1446 @raise: XendError for a device that cannot be migrated
1447 """
1448 for (n, c) in self.info['device']:
1449 rc = self.migrateDevice(n, c, live, dst, DEV_MIGRATE_TEST)
1450 if rc != 0:
1451 raise XendError("Device of type '%s' refuses migration." % n)
1453 def migrateDevices(self, live, dst, step, domName=''):
1454 """Notify the devices about migration
1455 """
1456 ctr = 0
1457 try:
1458 for (n, c) in self.info['device']:
1459 self.migrateDevice(n, c, live, dst, step, domName)
1460 ctr = ctr + 1
1461 except:
1462 for (n, c) in self.info['device']:
1463 if ctr == 0:
1464 step = step - 1
1465 ctr = ctr - 1
1466 self.recoverMigrateDevice(n, c, live, dst, step, domName)
1467 raise
1469 def migrateDevice(self, deviceClass, deviceConfig, live, dst, step, domName=''):
1470 return self.getDeviceController(deviceClass).migrate(deviceConfig, live, dst, step, domName)
1472 def recoverMigrateDevice(self, deviceClass, deviceConfig, live, dst, step, domName=''):
1473 return self.getDeviceController(deviceClass).recover_migrate(deviceConfig, live, dst, step, domName)
1475 def waitForDevices(self):
1476 """Wait for this domain's configured devices to connect.
1478 @raise: VmError if any device fails to initialise.
1479 """
1480 for c in controllerClasses:
1481 self.waitForDevices_(c)
1484 def device_create(self, dev_config):
1485 """Create a new device.
1487 @param dev_config: device configuration
1488 """
1489 dev_type = sxp.name(dev_config)
1490 devid = self.createDevice(dev_type, dev_config)
1491 self.waitForDevice(dev_type, devid)
1492 self.info['device'].append((dev_type, dev_config))
1493 return self.getDeviceController(dev_type).sxpr(devid)
1496 def device_configure(self, dev_config, devid):
1497 """Configure an existing device.
1498 @param dev_config: device configuration
1499 @param devid: device id
1500 """
1501 deviceClass = sxp.name(dev_config)
1502 self.reconfigureDevice(deviceClass, devid, dev_config)
1505 def pause(self):
1506 xc.domain_pause(self.domid)
1509 def unpause(self):
1510 xc.domain_unpause(self.domid)
1513 ## private:
1515 def restart(self, rename = False):
1516 """Restart the domain after it has exited.
1518 @param rename True if the old domain is to be renamed and preserved,
1519 False if it is to be destroyed.
1520 """
1522 self.configure_bootloader()
1523 config = self.sxpr()
1525 if self.readVm(RESTART_IN_PROGRESS):
1526 log.error('Xend failed during restart of domain %d. '
1527 'Refusing to restart to avoid loops.',
1528 self.domid)
1529 self.destroy()
1530 return
1532 self.writeVm(RESTART_IN_PROGRESS, 'True')
1534 now = time.time()
1535 rst = self.readVm('xend/previous_restart_time')
1536 if rst:
1537 rst = float(rst)
1538 timeout = now - rst
1539 if timeout < MINIMUM_RESTART_TIME:
1540 log.error(
1541 'VM %s restarting too fast (%f seconds since the last '
1542 'restart). Refusing to restart to avoid loops.',
1543 self.info['name'], timeout)
1544 self.destroy()
1545 return
1547 self.writeVm('xend/previous_restart_time', str(now))
1549 try:
1550 if rename:
1551 self.preserveForRestart()
1552 else:
1553 self.unwatchVm()
1554 self.destroyDomain()
1556 # new_dom's VM will be the same as this domain's VM, except where
1557 # the rename flag has instructed us to call preserveForRestart.
1558 # In that case, it is important that we remove the
1559 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1560 # once the new one is available.
1562 new_dom = None
1563 try:
1564 new_dom = XendDomain.instance().domain_create(config)
1565 new_dom.unpause()
1566 new_dom.removeVm(RESTART_IN_PROGRESS)
1567 except:
1568 if new_dom:
1569 new_dom.removeVm(RESTART_IN_PROGRESS)
1570 new_dom.destroy()
1571 else:
1572 self.removeVm(RESTART_IN_PROGRESS)
1573 raise
1574 except:
1575 log.exception('Failed to restart domain %d.', self.domid)
1578 def preserveForRestart(self):
1579 """Preserve a domain that has been shut down, by giving it a new UUID,
1580 cloning the VM details, and giving it a new name. This allows us to
1581 keep this domain for debugging, but restart a new one in its place
1582 preserving the restart semantics (name and UUID preserved).
1583 """
1585 new_name = self.generateUniqueName()
1586 new_uuid = uuid.toString(uuid.create())
1587 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1588 self.info['name'], self.domid, self.info['uuid'],
1589 new_name, new_uuid)
1590 self.unwatchVm()
1591 self.release_devices()
1592 self.info['name'] = new_name
1593 self.info['uuid'] = new_uuid
1594 self.vmpath = XendDomain.VMROOT + new_uuid
1595 self.storeVmDetails()
1596 self.preserve()
1599 def preserve(self):
1600 log.info("Preserving dead domain %s (%d).", self.info['name'],
1601 self.domid)
1602 self.unwatchVm()
1603 self.storeDom('xend/shutdown_completed', 'True')
1604 self.state_set(STATE_DOM_SHUTDOWN)
1607 # private:
1609 def generateUniqueName(self):
1610 n = 1
1611 while True:
1612 name = "%s-%d" % (self.info['name'], n)
1613 try:
1614 self.check_name(name)
1615 return name
1616 except VmError:
1617 n += 1
1620 def configure_bootloader(self):
1621 """Run the bootloader if we're configured to do so."""
1622 if not self.info['bootloader']:
1623 return
1624 blcfg = None
1625 # FIXME: this assumes that we want to use the first disk device
1626 for (n,c) in self.info['device']:
1627 if not n or not c or n != "vbd":
1628 continue
1629 disk = sxp.child_value(c, "uname")
1630 if disk is None:
1631 continue
1632 fn = blkdev_uname_to_file(disk)
1633 blcfg = bootloader(self.info['bootloader'], fn, 1)
1634 break
1635 if blcfg is None:
1636 msg = "Had a bootloader specified, but can't find disk"
1637 log.error(msg)
1638 raise VmError(msg)
1639 self.info['image'] = blcfg
1642 def send_sysrq(self, key):
1643 asserts.isCharConvertible(key)
1645 self.storeDom("control/sysrq", '%c' % key)
1648 def infoIsSet(self, name):
1649 return name in self.info and self.info[name] is not None
1652 #============================================================================
1653 # Register device controllers and their device config types.
1655 """A map from device-class names to the subclass of DevController that
1656 implements the device control specific to that device-class."""
1657 controllerClasses = {}
1659 def addControllerClass(device_class, cls):
1660 """Register a subclass of DevController to handle the named device-class.
1661 """
1662 cls.deviceClass = device_class
1663 controllerClasses[device_class] = cls
1666 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1667 addControllerClass('vbd', blkif.BlkifController)
1668 addControllerClass('vif', netif.NetifController)
1669 addControllerClass('vtpm', tpmif.TPMifController)
1670 addControllerClass('pci', pciif.PciController)
1671 addControllerClass('ioports', iopif.IOPortsController)
1672 addControllerClass('irq', irqif.IRQController)
1673 addControllerClass('usb', usbif.UsbifController)