ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 10743:08906834ca74

[powerpc] [xend] PPC doesn't support ballooning yet
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Jul 14 10:23:04 2006 +0100 (2006-07-14)
parents 2937703f0ed0
children f021b091c559
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
32 import os
34 import xen.lowlevel.xc
35 from xen.util import asserts
36 from xen.util.blkif import blkdev_uname_to_file
37 from xen.util import security
38 import balloon
39 import image
40 import sxp
41 import uuid
42 import XendDomain
43 import XendRoot
45 from xen.xend.XendBootloader import bootloader
46 from xen.xend.XendError import XendError, VmError
48 from xen.xend.xenstore.xstransact import xstransact, complete
49 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain
50 from xen.xend.xenstore.xswatch import xswatch
53 """Shutdown code for poweroff."""
54 DOMAIN_POWEROFF = 0
56 """Shutdown code for reboot."""
57 DOMAIN_REBOOT = 1
59 """Shutdown code for suspend."""
60 DOMAIN_SUSPEND = 2
62 """Shutdown code for crash."""
63 DOMAIN_CRASH = 3
65 """Shutdown code for halt."""
66 DOMAIN_HALT = 4
68 """Map shutdown codes to strings."""
69 shutdown_reasons = {
70 DOMAIN_POWEROFF: "poweroff",
71 DOMAIN_REBOOT : "reboot",
72 DOMAIN_SUSPEND : "suspend",
73 DOMAIN_CRASH : "crash",
74 DOMAIN_HALT : "halt"
75 }
77 restart_modes = [
78 "restart",
79 "destroy",
80 "preserve",
81 "rename-restart"
82 ]
84 STATE_DOM_OK = 1
85 STATE_DOM_SHUTDOWN = 2
87 SHUTDOWN_TIMEOUT = 30.0
89 ZOMBIE_PREFIX = 'Zombie-'
91 """Constants for the different stages of ext. device migration """
92 DEV_MIGRATE_TEST = 0
93 DEV_MIGRATE_STEP1 = 1
94 DEV_MIGRATE_STEP2 = 2
95 DEV_MIGRATE_STEP3 = 3
97 """Minimum time between domain restarts in seconds."""
98 MINIMUM_RESTART_TIME = 20
100 RESTART_IN_PROGRESS = 'xend/restart_in_progress'
103 xc = xen.lowlevel.xc.xc()
104 xroot = XendRoot.instance()
106 log = logging.getLogger("xend.XendDomainInfo")
107 #log.setLevel(logging.TRACE)
110 ##
111 # All parameters of VMs that may be configured on-the-fly, or at start-up.
112 #
113 VM_CONFIG_PARAMS = [
114 ('name', str),
115 ('on_poweroff', str),
116 ('on_reboot', str),
117 ('on_crash', str),
118 ]
121 ##
122 # Configuration entries that we expect to round-trip -- be read from the
123 # config file or xc, written to save-files (i.e. through sxpr), and reused as
124 # config on restart or restore, all without munging. Some configuration
125 # entries are munged for backwards compatibility reasons, or because they
126 # don't come out of xc in the same form as they are specified in the config
127 # file, so those are handled separately.
128 ROUNDTRIPPING_CONFIG_ENTRIES = [
129 ('uuid', str),
130 ('vcpus', int),
131 ('vcpu_avail', int),
132 ('cpu_weight', float),
133 ('memory', int),
134 ('maxmem', int),
135 ('bootloader', str),
136 ('bootloader_args', str),
137 ('features', str),
138 ('localtime', int),
139 ]
141 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
144 ##
145 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
146 # entries written to the store that cannot be reconfigured on-the-fly.
147 #
148 VM_STORE_ENTRIES = [
149 ('uuid', str),
150 ('vcpus', int),
151 ('vcpu_avail', int),
152 ('memory', int),
153 ('maxmem', int),
154 ('start_time', float),
155 ]
157 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
160 #
161 # There are a number of CPU-related fields:
162 #
163 # vcpus: the number of virtual CPUs this domain is configured to use.
164 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
165 # its VCPUs. This is translated to
166 # <dompath>/cpu/<id>/availability = {online,offline} for use
167 # by the guest domain.
168 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
169 # CPUs that that VCPU may use.
170 # cpu: a configuration setting requesting that VCPU 0 is pinned to
171 # the specified physical CPU.
172 #
173 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
174 # across save, restore, migrate, and restart). The other settings are only
175 # specific to the domain, so are lost when the VM moves.
176 #
179 def create(config):
180 """Create a VM from a configuration.
182 @param config configuration
183 @raise: VmError for invalid configuration
184 """
186 log.debug("XendDomainInfo.create(%s)", config)
188 vm = XendDomainInfo(parseConfig(config))
189 try:
190 vm.construct()
191 vm.initDomain()
192 vm.storeVmDetails()
193 vm.storeDomDetails()
194 vm.registerWatches()
195 vm.refreshShutdown()
196 return vm
197 except:
198 log.exception('Domain construction failed')
199 vm.destroy()
200 raise
203 def recreate(xeninfo, priv):
204 """Create the VM object for an existing domain. The domain must not
205 be dying, as the paths in the store should already have been removed,
206 and asking us to recreate them causes problems."""
208 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
210 assert not xeninfo['dying']
212 domid = xeninfo['dom']
213 uuid1 = xeninfo['handle']
214 xeninfo['uuid'] = uuid.toString(uuid1)
215 dompath = GetDomainPath(domid)
216 if not dompath:
217 raise XendError(
218 'No domain path in store for existing domain %d' % domid)
220 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
221 try:
222 vmpath = xstransact.Read(dompath, "vm")
223 if not vmpath:
224 raise XendError(
225 'No vm path in store for existing domain %d' % domid)
226 uuid2_str = xstransact.Read(vmpath, "uuid")
227 if not uuid2_str:
228 raise XendError(
229 'No vm/uuid path in store for existing domain %d' % domid)
231 uuid2 = uuid.fromString(uuid2_str)
233 if uuid1 != uuid2:
234 raise XendError(
235 'Uuid in store does not match uuid for existing domain %d: '
236 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
238 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
240 except Exception, exn:
241 if priv:
242 log.warn(str(exn))
244 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
245 vm.recreateDom()
246 vm.removeVm()
247 vm.storeVmDetails()
248 vm.storeDomDetails()
250 vm.registerWatches()
251 vm.refreshShutdown(xeninfo)
252 return vm
255 def restore(config):
256 """Create a domain and a VM object to do a restore.
258 @param config: domain configuration
259 """
261 log.debug("XendDomainInfo.restore(%s)", config)
263 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
264 try:
265 vm.construct()
266 vm.storeVmDetails()
267 vm.createDevices()
268 vm.createChannels()
269 vm.storeDomDetails()
270 vm.endRestore()
271 return vm
272 except:
273 vm.destroy()
274 raise
277 def parseConfig(config):
278 def get_cfg(name, conv = None):
279 val = sxp.child_value(config, name)
281 if conv and not val is None:
282 try:
283 return conv(val)
284 except TypeError, exn:
285 raise VmError(
286 'Invalid setting %s = %s in configuration: %s' %
287 (name, val, str(exn)))
288 else:
289 return val
292 log.debug("parseConfig: config is %s", config)
294 result = {}
296 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
297 result[e[0]] = get_cfg(e[0], e[1])
299 result['cpu'] = get_cfg('cpu', int)
300 result['cpus'] = get_cfg('cpus', str)
301 result['image'] = get_cfg('image')
302 tmp_security = get_cfg('security')
303 if tmp_security:
304 result['security'] = tmp_security
306 try:
307 if result['image']:
308 v = sxp.child_value(result['image'], 'vcpus')
309 if result['vcpus'] is None and v is not None:
310 result['vcpus'] = int(v)
311 elif v is not None and int(v) != result['vcpus']:
312 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
313 ' Using %s VCPUs for VM %s.') %
314 (result['vcpus'], v, result['uuid']))
315 result['vcpus'] = int(v)
316 except TypeError, exn:
317 raise VmError(
318 'Invalid configuration setting: vcpus = %s: %s' %
319 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
321 try:
322 # support legacy config files with 'cpu' parameter
323 # NB: prepending to list to support previous behavior
324 # where 'cpu' parameter pinned VCPU0.
325 if result['cpu']:
326 if result['cpus']:
327 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
328 else:
329 result['cpus'] = str(result['cpu'])
331 # convert 'cpus' string to list of ints
332 # 'cpus' supports a list of ranges (0-3), seperated by
333 # commas, and negation, (^1).
334 # Precedence is settled by order of the string:
335 # "0-3,^1" -> [0,2,3]
336 # "0-3,^1,1" -> [0,1,2,3]
337 if result['cpus']:
338 cpus = []
339 for c in result['cpus'].split(','):
340 if c.find('-') != -1:
341 (x,y) = c.split('-')
342 for i in range(int(x),int(y)+1):
343 cpus.append(int(i))
344 else:
345 # remove this element from the list
346 if c[0] == '^':
347 cpus = [x for x in cpus if x != int(c[1:])]
348 else:
349 cpus.append(int(c))
351 result['cpus'] = cpus
353 except ValueError, exn:
354 raise VmError(
355 'Invalid configuration setting: cpus = %s: %s' %
356 (result['cpus'], exn))
358 result['backend'] = []
359 for c in sxp.children(config, 'backend'):
360 result['backend'].append(sxp.name(sxp.child0(c)))
362 result['device'] = []
363 for d in sxp.children(config, 'device'):
364 c = sxp.child0(d)
365 result['device'].append((sxp.name(c), c))
367 # Configuration option "restart" is deprecated. Parse it, but
368 # let on_xyz override it if they are present.
369 restart = get_cfg('restart')
370 if restart:
371 def handle_restart(event, val):
372 if result[event] is None:
373 result[event] = val
375 if restart == "onreboot":
376 handle_restart('on_poweroff', 'destroy')
377 handle_restart('on_reboot', 'restart')
378 handle_restart('on_crash', 'destroy')
379 elif restart == "always":
380 handle_restart('on_poweroff', 'restart')
381 handle_restart('on_reboot', 'restart')
382 handle_restart('on_crash', 'restart')
383 elif restart == "never":
384 handle_restart('on_poweroff', 'destroy')
385 handle_restart('on_reboot', 'destroy')
386 handle_restart('on_crash', 'destroy')
387 else:
388 log.warn("Ignoring malformed and deprecated config option "
389 "restart = %s", restart)
391 log.debug("parseConfig: result is %s", result)
392 return result
395 def domain_by_name(name):
396 return XendDomain.instance().domain_lookup_by_name_nr(name)
399 def shutdown_reason(code):
400 """Get a shutdown reason from a code.
402 @param code: shutdown code
403 @type code: int
404 @return: shutdown reason
405 @rtype: string
406 """
407 return shutdown_reasons.get(code, "?")
409 def dom_get(dom):
410 """Get info from xen for an existing domain.
412 @param dom: domain id
413 @return: info or None
414 """
415 try:
416 domlist = xc.domain_getinfo(dom, 1)
417 if domlist and dom == domlist[0]['dom']:
418 return domlist[0]
419 except Exception, err:
420 # ignore missing domain
421 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
422 return None
425 class XendDomainInfo:
427 def __init__(self, info, domid = None, dompath = None, augment = False,
428 priv = False, resume = False):
430 self.info = info
432 if not self.infoIsSet('uuid'):
433 self.info['uuid'] = uuid.toString(uuid.create())
435 if domid is not None:
436 self.domid = domid
437 elif 'dom' in info:
438 self.domid = int(info['dom'])
439 else:
440 self.domid = None
442 self.vmpath = XendDomain.VMROOT + self.info['uuid']
443 self.dompath = dompath
445 if augment:
446 self.augmentInfo(priv)
448 self.validateInfo()
450 self.image = None
451 self.security = None
452 self.store_port = None
453 self.store_mfn = None
454 self.console_port = None
455 self.console_mfn = None
457 self.vmWatch = None
458 self.shutdownWatch = None
460 self.shutdownStartTime = None
462 self.state = STATE_DOM_OK
463 self.state_updated = threading.Condition()
464 self.refresh_shutdown_lock = threading.Condition()
466 self.setResume(resume)
468 ## private:
470 def readVMDetails(self, params):
471 """Read the specified parameters from the store.
472 """
473 try:
474 return self.gatherVm(*params)
475 except ValueError:
476 # One of the int/float entries in params has a corresponding store
477 # entry that is invalid. We recover, because older versions of
478 # Xend may have put the entry there (memory/target, for example),
479 # but this is in general a bad situation to have reached.
480 log.exception(
481 "Store corrupted at %s! Domain %d's configuration may be "
482 "affected.", self.vmpath, self.domid)
483 return []
486 def storeChanged(self, _):
487 log.trace("XendDomainInfo.storeChanged");
489 changed = False
491 def f(x, y):
492 if y is not None and self.info[x[0]] != y:
493 self.info[x[0]] = y
494 changed = True
496 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
498 im = self.readVm('image')
499 current_im = self.info['image']
500 if (im is not None and
501 (current_im is None or sxp.to_string(current_im) != im)):
502 self.info['image'] = sxp.from_string(im)
503 changed = True
505 if changed:
506 # Update the domain section of the store, as this contains some
507 # parameters derived from the VM configuration.
508 self.storeDomDetails()
510 return 1
513 def augmentInfo(self, priv):
514 """Augment self.info, as given to us through {@link #recreate}, with
515 values taken from the store. This recovers those values known to xend
516 but not to the hypervisor.
517 """
518 def useIfNeeded(name, val):
519 if not self.infoIsSet(name) and val is not None:
520 self.info[name] = val
522 if priv:
523 entries = VM_STORE_ENTRIES[:]
524 entries.remove(('memory', int))
525 entries.remove(('maxmem', int))
526 else:
527 entries = VM_STORE_ENTRIES
528 entries.append(('image', str))
529 entries.append(('security', str))
531 map(lambda x, y: useIfNeeded(x[0], y), entries,
532 self.readVMDetails(entries))
534 device = []
535 for c in controllerClasses:
536 devconfig = self.getDeviceConfigurations(c)
537 if devconfig:
538 device.extend(map(lambda x: (c, x), devconfig))
539 useIfNeeded('device', device)
542 def validateInfo(self):
543 """Validate and normalise the info block. This has either been parsed
544 by parseConfig, or received from xc through recreate and augmented by
545 the current store contents.
546 """
547 def defaultInfo(name, val):
548 if not self.infoIsSet(name):
549 self.info[name] = val()
551 try:
552 defaultInfo('name', lambda: "Domain-%d" % self.domid)
553 defaultInfo('on_poweroff', lambda: "destroy")
554 defaultInfo('on_reboot', lambda: "restart")
555 defaultInfo('on_crash', lambda: "restart")
556 defaultInfo('features', lambda: "")
557 defaultInfo('cpu', lambda: None)
558 defaultInfo('cpus', lambda: [])
559 defaultInfo('cpu_weight', lambda: 1.0)
561 # some domains don't have a config file (e.g. dom0 )
562 # to set number of vcpus so we derive available cpus
563 # from max_vcpu_id which is present for running domains.
564 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
565 avail = int(self.info['max_vcpu_id'])+1
566 else:
567 avail = int(1)
569 defaultInfo('vcpus', lambda: avail)
570 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
571 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
572 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
574 defaultInfo('memory', lambda: 0)
575 defaultInfo('maxmem', lambda: 0)
576 defaultInfo('bootloader', lambda: None)
577 defaultInfo('bootloader_args', lambda: None)
578 defaultInfo('backend', lambda: [])
579 defaultInfo('device', lambda: [])
580 defaultInfo('image', lambda: None)
581 defaultInfo('security', lambda: None)
583 self.check_name(self.info['name'])
585 if isinstance(self.info['image'], str):
586 self.info['image'] = sxp.from_string(self.info['image'])
588 if isinstance(self.info['security'], str):
589 self.info['security'] = sxp.from_string(self.info['security'])
591 if self.info['memory'] == 0:
592 if self.infoIsSet('mem_kb'):
593 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
595 if self.info['maxmem'] < self.info['memory']:
596 self.info['maxmem'] = self.info['memory']
598 for (n, c) in self.info['device']:
599 if not n or not c or n not in controllerClasses:
600 raise VmError('invalid device (%s, %s)' %
601 (str(n), str(c)))
603 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
604 if self.info[event] not in restart_modes:
605 raise VmError('invalid restart event: %s = %s' %
606 (event, str(self.info[event])))
608 except KeyError, exn:
609 log.exception(exn)
610 raise VmError('Unspecified domain detail: %s' % exn)
613 def readVm(self, *args):
614 return xstransact.Read(self.vmpath, *args)
616 def writeVm(self, *args):
617 return xstransact.Write(self.vmpath, *args)
619 def removeVm(self, *args):
620 return xstransact.Remove(self.vmpath, *args)
622 def gatherVm(self, *args):
623 return xstransact.Gather(self.vmpath, *args)
626 ## public:
628 def storeVm(self, *args):
629 return xstransact.Store(self.vmpath, *args)
632 ## private:
634 def readDom(self, *args):
635 return xstransact.Read(self.dompath, *args)
637 def writeDom(self, *args):
638 return xstransact.Write(self.dompath, *args)
641 ## public:
643 def removeDom(self, *args):
644 return xstransact.Remove(self.dompath, *args)
646 def recreateDom(self):
647 complete(self.dompath, lambda t: self._recreateDom(t))
649 def _recreateDom(self, t):
650 t.remove()
651 t.mkdir()
652 t.set_permissions({ 'dom' : self.domid })
655 ## private:
657 def storeDom(self, *args):
658 return xstransact.Store(self.dompath, *args)
661 ## public:
663 def completeRestore(self, store_mfn, console_mfn):
665 log.debug("XendDomainInfo.completeRestore")
667 self.store_mfn = store_mfn
668 self.console_mfn = console_mfn
670 self.introduceDomain()
671 self.storeDomDetails()
672 self.registerWatches()
673 self.refreshShutdown()
675 log.debug("XendDomainInfo.completeRestore done")
678 def storeVmDetails(self):
679 to_store = {}
681 for k in VM_STORE_ENTRIES:
682 if self.infoIsSet(k[0]):
683 to_store[k[0]] = str(self.info[k[0]])
685 if self.infoIsSet('image'):
686 to_store['image'] = sxp.to_string(self.info['image'])
688 if self.infoIsSet('security'):
689 security = self.info['security']
690 to_store['security'] = sxp.to_string(security)
691 for idx in range(0, len(security)):
692 if security[idx][0] == 'access_control':
693 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
694 for aidx in range(1, len(security[idx])):
695 if security[idx][aidx][0] == 'label':
696 to_store['security/access_control/label'] = security[idx][aidx][1]
697 if security[idx][aidx][0] == 'policy':
698 to_store['security/access_control/policy'] = security[idx][aidx][1]
699 if security[idx][0] == 'ssidref':
700 to_store['security/ssidref'] = str(security[idx][1])
702 log.debug("Storing VM details: %s", to_store)
704 self.writeVm(to_store)
705 self.setVmPermissions()
708 def setVmPermissions(self):
709 """Allow the guest domain to read its UUID. We don't allow it to
710 access any other entry, for security."""
711 xstransact.SetPermissions('%s/uuid' % self.vmpath,
712 { 'dom' : self.domid,
713 'read' : True,
714 'write' : False })
717 def storeDomDetails(self):
718 to_store = {
719 'domid': str(self.domid),
720 'vm': self.vmpath,
721 'name': self.info['name'],
722 'console/limit': str(xroot.get_console_limit() * 1024),
723 'memory/target': str(self.info['memory'] * 1024)
724 }
726 def f(n, v):
727 if v is not None:
728 to_store[n] = str(v)
730 f('console/port', self.console_port)
731 f('console/ring-ref', self.console_mfn)
732 f('store/port', self.store_port)
733 f('store/ring-ref', self.store_mfn)
735 to_store.update(self.vcpuDomDetails())
737 log.debug("Storing domain details: %s", to_store)
739 self.writeDom(to_store)
742 ## private:
744 def vcpuDomDetails(self):
745 def availability(n):
746 if self.info['vcpu_avail'] & (1 << n):
747 return 'online'
748 else:
749 return 'offline'
751 result = {}
752 for v in range(0, self.info['vcpus']):
753 result["cpu/%d/availability" % v] = availability(v)
754 return result
757 ## public:
759 def registerWatches(self):
760 """Register a watch on this VM's entries in the store, and the
761 domain's control/shutdown node, so that when they are changed
762 externally, we keep up to date. This should only be called by {@link
763 #create}, {@link #recreate}, or {@link #restore}, once the domain's
764 details have been written, but before the new instance is returned."""
765 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
766 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
767 self.handleShutdownWatch)
770 def getDomid(self):
771 return self.domid
773 def setName(self, name):
774 self.check_name(name)
775 self.info['name'] = name
776 self.storeVm("name", name)
778 def getName(self):
779 return self.info['name']
781 def getDomainPath(self):
782 return self.dompath
785 def getStorePort(self):
786 """For use only by image.py and XendCheckpoint.py."""
787 return self.store_port
790 def getConsolePort(self):
791 """For use only by image.py and XendCheckpoint.py"""
792 return self.console_port
794 def getFeatures(self):
795 """For use only by image.py."""
796 return self.info['features']
798 def getVCpuCount(self):
799 return self.info['vcpus']
802 def setVCpuCount(self, vcpus):
803 self.info['vcpu_avail'] = (1 << vcpus) - 1
804 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
805 self.writeDom(self.vcpuDomDetails())
807 def getLabel(self):
808 return security.get_security_info(self.info, 'label')
810 def getMemoryTarget(self):
811 """Get this domain's target memory size, in KB."""
812 return self.info['memory'] * 1024
814 def getResume(self):
815 return "%s" % self.info['resume']
817 def endRestore(self):
818 self.setResume(False)
820 def setResume(self, state):
821 self.info['resume'] = state
823 def refreshShutdown(self, xeninfo = None):
824 # If set at the end of this method, a restart is required, with the
825 # given reason. This restart has to be done out of the scope of
826 # refresh_shutdown_lock.
827 restart_reason = None
829 self.refresh_shutdown_lock.acquire()
830 try:
831 if xeninfo is None:
832 xeninfo = dom_get(self.domid)
833 if xeninfo is None:
834 # The domain no longer exists. This will occur if we have
835 # scheduled a timer to check for shutdown timeouts and the
836 # shutdown succeeded. It will also occur if someone
837 # destroys a domain beneath us. We clean up the domain,
838 # just in case, but we can't clean up the VM, because that
839 # VM may have migrated to a different domain on this
840 # machine.
841 self.cleanupDomain()
842 return
844 if xeninfo['dying']:
845 # Dying means that a domain has been destroyed, but has not
846 # yet been cleaned up by Xen. This state could persist
847 # indefinitely if, for example, another domain has some of its
848 # pages mapped. We might like to diagnose this problem in the
849 # future, but for now all we do is make sure that it's not us
850 # holding the pages, by calling cleanupDomain. We can't
851 # clean up the VM, as above.
852 self.cleanupDomain()
853 return
855 elif xeninfo['crashed']:
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 log.warn('Domain has crashed: name=%s id=%d.',
862 self.info['name'], self.domid)
864 if xroot.get_enable_dump():
865 self.dumpCore()
867 restart_reason = 'crash'
869 elif xeninfo['shutdown']:
870 if self.readDom('xend/shutdown_completed'):
871 # We've seen this shutdown already, but we are preserving
872 # the domain for debugging. Leave it alone.
873 return
875 else:
876 reason = shutdown_reason(xeninfo['shutdown_reason'])
878 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
879 self.info['name'], self.domid, reason)
881 self.clearRestart()
883 if reason == 'suspend':
884 self.state_set(STATE_DOM_SHUTDOWN)
885 # Don't destroy the domain. XendCheckpoint will do
886 # this once it has finished. However, stop watching
887 # the VM path now, otherwise we will end up with one
888 # watch for the old domain, and one for the new.
889 self.unwatchVm()
890 elif reason in ['poweroff', 'reboot']:
891 restart_reason = reason
892 else:
893 self.destroy()
895 elif self.dompath is None:
896 # We have yet to manage to call introduceDomain on this
897 # domain. This can happen if a restore is in progress, or has
898 # failed. Ignore this domain.
899 pass
900 else:
901 # Domain is alive. If we are shutting it down, then check
902 # the timeout on that, and destroy it if necessary.
904 if self.shutdownStartTime:
905 timeout = (SHUTDOWN_TIMEOUT - time.time() +
906 self.shutdownStartTime)
907 if timeout < 0:
908 log.info(
909 "Domain shutdown timeout expired: name=%s id=%s",
910 self.info['name'], self.domid)
911 self.destroy()
912 finally:
913 self.refresh_shutdown_lock.release()
915 if restart_reason:
916 self.maybeRestart(restart_reason)
919 def handleShutdownWatch(self, _):
920 log.debug('XendDomainInfo.handleShutdownWatch')
922 reason = self.readDom('control/shutdown')
924 if reason and reason != 'suspend':
925 sst = self.readDom('xend/shutdown_start_time')
926 now = time.time()
927 if sst:
928 self.shutdownStartTime = float(sst)
929 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
930 else:
931 self.shutdownStartTime = now
932 self.storeDom('xend/shutdown_start_time', now)
933 timeout = SHUTDOWN_TIMEOUT
935 log.trace(
936 "Scheduling refreshShutdown on domain %d in %ds.",
937 self.domid, timeout)
938 threading.Timer(timeout, self.refreshShutdown).start()
940 return True
943 def shutdown(self, reason):
944 if not reason in shutdown_reasons.values():
945 raise XendError('Invalid reason: %s' % reason)
946 self.storeDom("control/shutdown", reason)
949 ## private:
951 def clearRestart(self):
952 self.removeDom("xend/shutdown_start_time")
955 def maybeRestart(self, reason):
956 # Dispatch to the correct method based upon the configured on_{reason}
957 # behaviour.
958 {"destroy" : self.destroy,
959 "restart" : self.restart,
960 "preserve" : self.preserve,
961 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
964 def renameRestart(self):
965 self.restart(True)
968 def dumpCore(self):
969 """Create a core dump for this domain. Nothrow guarantee."""
971 try:
972 corefile = "/var/xen/dump/%s.%s.core" % (self.info['name'],
973 self.domid)
974 xc.domain_dumpcore(self.domid, corefile)
976 except:
977 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
978 self.domid, self.info['name'])
981 ## public:
983 def setMemoryTarget(self, target):
984 """Set the memory target of this domain.
985 @param target In MiB.
986 """
987 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
988 self.info['name'], self.domid, target)
990 self.info['memory'] = target
991 self.storeVm("memory", target)
992 self.storeDom("memory/target", target << 10)
995 def update(self, info = None):
996 """Update with info from xc.domain_getinfo().
997 """
999 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1000 if not info:
1001 info = dom_get(self.domid)
1002 if not info:
1003 return
1005 #manually update ssidref / security fields
1006 if security.on() and info.has_key('ssidref'):
1007 if (info['ssidref'] != 0) and self.info.has_key('security'):
1008 security_field = self.info['security']
1009 if not security_field:
1010 #create new security element
1011 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1012 #ssidref field not used any longer
1013 info.pop('ssidref')
1015 self.info.update(info)
1016 self.validateInfo()
1017 self.refreshShutdown(info)
1019 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1020 self.info)
1023 ## private:
1025 def state_set(self, state):
1026 self.state_updated.acquire()
1027 try:
1028 if self.state != state:
1029 self.state = state
1030 self.state_updated.notifyAll()
1031 finally:
1032 self.state_updated.release()
1035 ## public:
1037 def waitForShutdown(self):
1038 self.state_updated.acquire()
1039 try:
1040 while self.state == STATE_DOM_OK:
1041 self.state_updated.wait()
1042 finally:
1043 self.state_updated.release()
1046 def __str__(self):
1047 s = "<domain"
1048 s += " id=" + str(self.domid)
1049 s += " name=" + self.info['name']
1050 s += " memory=" + str(self.info['memory'])
1051 s += ">"
1052 return s
1054 __repr__ = __str__
1057 ## private:
1059 def createDevice(self, deviceClass, devconfig):
1060 return self.getDeviceController(deviceClass).createDevice(devconfig)
1063 def waitForDevices_(self, deviceClass):
1064 return self.getDeviceController(deviceClass).waitForDevices()
1067 def waitForDevice(self, deviceClass, devid):
1068 return self.getDeviceController(deviceClass).waitForDevice(devid)
1071 def reconfigureDevice(self, deviceClass, devid, devconfig):
1072 return self.getDeviceController(deviceClass).reconfigureDevice(
1073 devid, devconfig)
1076 ## public:
1078 def destroyDevice(self, deviceClass, devid):
1079 return self.getDeviceController(deviceClass).destroyDevice(devid)
1082 def getDeviceSxprs(self, deviceClass):
1083 return self.getDeviceController(deviceClass).sxprs()
1086 ## private:
1088 def getDeviceConfigurations(self, deviceClass):
1089 return self.getDeviceController(deviceClass).configurations()
1092 def getDeviceController(self, name):
1093 if name not in controllerClasses:
1094 raise XendError("unknown device type: " + str(name))
1096 return controllerClasses[name](self)
1099 ## public:
1101 def sxpr(self):
1102 sxpr = ['domain',
1103 ['domid', self.domid]]
1105 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1106 if self.infoIsSet(e[0]):
1107 sxpr.append([e[0], self.info[e[0]]])
1109 if self.infoIsSet('image'):
1110 sxpr.append(['image', self.info['image']])
1112 if self.infoIsSet('security'):
1113 sxpr.append(['security', self.info['security']])
1115 for cls in controllerClasses:
1116 for config in self.getDeviceConfigurations(cls):
1117 sxpr.append(['device', config])
1119 def stateChar(name):
1120 if name in self.info:
1121 if self.info[name]:
1122 return name[0]
1123 else:
1124 return '-'
1125 else:
1126 return '?'
1128 state = reduce(
1129 lambda x, y: x + y,
1130 map(stateChar,
1131 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1132 'dying']))
1134 sxpr.append(['state', state])
1135 if self.infoIsSet('shutdown'):
1136 reason = shutdown_reason(self.info['shutdown_reason'])
1137 sxpr.append(['shutdown_reason', reason])
1138 if self.infoIsSet('cpu_time'):
1139 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1140 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1142 if self.infoIsSet('start_time'):
1143 up_time = time.time() - self.info['start_time']
1144 sxpr.append(['up_time', str(up_time) ])
1145 sxpr.append(['start_time', str(self.info['start_time']) ])
1147 if self.store_mfn:
1148 sxpr.append(['store_mfn', self.store_mfn])
1149 if self.console_mfn:
1150 sxpr.append(['console_mfn', self.console_mfn])
1152 return sxpr
1155 def getVCPUInfo(self):
1156 try:
1157 # We include the domain name and ID, to help xm.
1158 sxpr = ['domain',
1159 ['domid', self.domid],
1160 ['name', self.info['name']],
1161 ['vcpu_count', self.info['online_vcpus']]]
1163 for i in range(0, self.info['max_vcpu_id']+1):
1164 info = xc.vcpu_getinfo(self.domid, i)
1166 sxpr.append(['vcpu',
1167 ['number', i],
1168 ['online', info['online']],
1169 ['blocked', info['blocked']],
1170 ['running', info['running']],
1171 ['cpu_time', info['cpu_time'] / 1e9],
1172 ['cpu', info['cpu']],
1173 ['cpumap', info['cpumap']]])
1175 return sxpr
1177 except RuntimeError, exn:
1178 raise XendError(str(exn))
1181 ## private:
1183 def check_name(self, name):
1184 """Check if a vm name is valid. Valid names contain alphabetic characters,
1185 digits, or characters in '_-.:/+'.
1186 The same name cannot be used for more than one vm at the same time.
1188 @param name: name
1189 @raise: VmError if invalid
1190 """
1191 if name is None or name == '':
1192 raise VmError('missing vm name')
1193 for c in name:
1194 if c in string.digits: continue
1195 if c in '_-.:/+': continue
1196 if c in string.ascii_letters: continue
1197 raise VmError('invalid vm name')
1199 dominfo = domain_by_name(name)
1200 if not dominfo:
1201 return
1202 if self.domid is None:
1203 raise VmError("VM name '%s' already in use by domain %d" %
1204 (name, dominfo.domid))
1205 if dominfo.domid != self.domid:
1206 raise VmError("VM name '%s' is used in both domains %d and %d" %
1207 (name, self.domid, dominfo.domid))
1210 def construct(self):
1211 """Construct the domain.
1213 @raise: VmError on error
1214 """
1216 log.debug('XendDomainInfo.construct: %s',
1217 self.domid)
1219 self.domid = xc.domain_create(
1220 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1221 handle = uuid.fromString(self.info['uuid']))
1223 if self.domid < 0:
1224 raise VmError('Creating domain failed: name=%s' %
1225 self.info['name'])
1227 self.dompath = GetDomainPath(self.domid)
1229 self.recreateDom()
1231 # Set maximum number of vcpus in domain
1232 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1235 def introduceDomain(self):
1236 assert self.domid is not None
1237 assert self.store_mfn is not None
1238 assert self.store_port is not None
1240 try:
1241 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1242 except RuntimeError, exn:
1243 raise XendError(str(exn))
1246 def initDomain(self):
1247 log.debug('XendDomainInfo.initDomain: %s %s',
1248 self.domid,
1249 self.info['cpu_weight'])
1251 # if we have a boot loader but no image, then we need to set things
1252 # up by running the boot loader non-interactively
1253 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1254 self.configure_bootloader()
1256 if not self.infoIsSet('image'):
1257 raise VmError('Missing image in configuration')
1259 try:
1260 self.image = image.create(self,
1261 self.info['image'],
1262 self.info['device'])
1264 localtime = self.info['localtime']
1265 if localtime is not None and localtime == 1:
1266 xc.domain_set_time_offset(self.domid)
1268 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1270 # repin domain vcpus if a restricted cpus list is provided
1271 # this is done prior to memory allocation to aide in memory
1272 # distribution for NUMA systems.
1273 cpus = self.info['cpus']
1274 if cpus is not None and len(cpus) > 0:
1275 for v in range(0, self.info['max_vcpu_id']+1):
1276 # pincpu takes a list of ints
1277 cpu = [ int( cpus[v % len(cpus)] ) ]
1278 xc.vcpu_setaffinity(self.domid, v, cpu)
1280 # set domain maxmem in KiB
1281 xc.domain_setmaxmem(self.domid, self.info['maxmem'] * 1024)
1283 m = self.image.getDomainMemory(self.info['memory'] * 1024)
1284 balloon.free(m)
1286 init_reservation = self.info['memory'] * 1024
1287 if os.uname()[4] in ('ia64', 'ppc64'):
1288 # Workaround for architectures that don't yet support
1289 # ballooning.
1290 init_reservation = m
1292 xc.domain_memory_increase_reservation(self.domid, init_reservation,
1293 0, 0)
1295 self.createChannels()
1297 channel_details = self.image.createImage()
1299 self.store_mfn = channel_details['store_mfn']
1300 if 'console_mfn' in channel_details:
1301 self.console_mfn = channel_details['console_mfn']
1303 self.introduceDomain()
1305 self.createDevices()
1307 if self.info['bootloader']:
1308 self.image.cleanupBootloading()
1310 self.info['start_time'] = time.time()
1312 except RuntimeError, exn:
1313 raise VmError(str(exn))
1316 ## public:
1318 def cleanupDomain(self):
1319 """Cleanup domain resources; release devices. Idempotent. Nothrow
1320 guarantee."""
1322 self.refresh_shutdown_lock.acquire()
1323 try:
1324 self.unwatchShutdown()
1326 self.release_devices()
1328 if self.image:
1329 try:
1330 self.image.destroy()
1331 except:
1332 log.exception(
1333 "XendDomainInfo.cleanup: image.destroy() failed.")
1334 self.image = None
1336 try:
1337 self.removeDom()
1338 except:
1339 log.exception("Removing domain path failed.")
1341 try:
1342 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1343 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1344 except:
1345 log.exception("Renaming Zombie failed.")
1347 self.state_set(STATE_DOM_SHUTDOWN)
1348 finally:
1349 self.refresh_shutdown_lock.release()
1352 def cleanupVm(self):
1353 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1355 self.unwatchVm()
1357 try:
1358 self.removeVm()
1359 except:
1360 log.exception("Removing VM path failed.")
1363 ## private:
1365 def unwatchVm(self):
1366 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1367 guarantee."""
1369 try:
1370 try:
1371 if self.vmWatch:
1372 self.vmWatch.unwatch()
1373 finally:
1374 self.vmWatch = None
1375 except:
1376 log.exception("Unwatching VM path failed.")
1379 def unwatchShutdown(self):
1380 """Remove the watch on the domain's control/shutdown node, if any.
1381 Idempotent. Nothrow guarantee. Expects to be protected by the
1382 refresh_shutdown_lock."""
1384 try:
1385 try:
1386 if self.shutdownWatch:
1387 self.shutdownWatch.unwatch()
1388 finally:
1389 self.shutdownWatch = None
1390 except:
1391 log.exception("Unwatching control/shutdown failed.")
1394 ## public:
1396 def destroy(self):
1397 """Cleanup VM and destroy domain. Nothrow guarantee."""
1399 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1401 self.cleanupVm()
1402 if self.dompath is not None:
1403 self.destroyDomain()
1406 def destroyDomain(self):
1407 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1409 try:
1410 if self.domid is not None:
1411 xc.domain_destroy(self.domid)
1412 except:
1413 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1415 self.cleanupDomain()
1418 ## private:
1420 def release_devices(self):
1421 """Release all domain's devices. Nothrow guarantee."""
1423 while True:
1424 t = xstransact("%s/device" % self.dompath)
1425 for n in controllerClasses.keys():
1426 for d in t.list(n):
1427 try:
1428 t.remove(d)
1429 except:
1430 # Log and swallow any exceptions in removal --
1431 # there's nothing more we can do.
1432 log.exception(
1433 "Device release failed: %s; %s; %s",
1434 self.info['name'], n, d)
1435 if t.commit():
1436 break
1439 def createChannels(self):
1440 """Create the channels to the domain.
1441 """
1442 self.store_port = self.createChannel()
1443 self.console_port = self.createChannel()
1446 def createChannel(self):
1447 """Create an event channel to the domain.
1448 """
1449 try:
1450 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1451 except:
1452 log.exception("Exception in alloc_unbound(%d)", self.domid)
1453 raise
1456 ## public:
1458 def createDevices(self):
1459 """Create the devices for a vm.
1461 @raise: VmError for invalid devices
1462 """
1464 for (n, c) in self.info['device']:
1465 self.createDevice(n, c)
1467 if self.image:
1468 self.image.createDeviceModel()
1470 ## public:
1472 def testMigrateDevices(self, network, dst):
1473 """ Notify all device about intention of migration
1474 @raise: XendError for a device that cannot be migrated
1475 """
1476 for (n, c) in self.info['device']:
1477 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1478 if rc != 0:
1479 raise XendError("Device of type '%s' refuses migration." % n)
1481 def migrateDevices(self, network, dst, step, domName=''):
1482 """Notify the devices about migration
1483 """
1484 ctr = 0
1485 try:
1486 for (n, c) in self.info['device']:
1487 self.migrateDevice(n, c, network, dst, step, domName)
1488 ctr = ctr + 1
1489 except:
1490 for (n, c) in self.info['device']:
1491 if ctr == 0:
1492 step = step - 1
1493 ctr = ctr - 1
1494 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1495 raise
1497 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1498 step, domName=''):
1499 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1500 network, dst, step, domName)
1502 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1503 dst, step, domName=''):
1504 return self.getDeviceController(deviceClass).recover_migrate(
1505 deviceConfig, network, dst, step, domName)
1507 def waitForDevices(self):
1508 """Wait for this domain's configured devices to connect.
1510 @raise: VmError if any device fails to initialise.
1511 """
1512 for c in controllerClasses:
1513 self.waitForDevices_(c)
1516 def device_create(self, dev_config):
1517 """Create a new device.
1519 @param dev_config: device configuration
1520 """
1521 dev_type = sxp.name(dev_config)
1522 devid = self.createDevice(dev_type, dev_config)
1523 self.waitForDevice(dev_type, devid)
1524 self.info['device'].append((dev_type, dev_config))
1525 return self.getDeviceController(dev_type).sxpr(devid)
1528 def device_configure(self, dev_config, devid):
1529 """Configure an existing device.
1530 @param dev_config: device configuration
1531 @param devid: device id
1532 """
1533 deviceClass = sxp.name(dev_config)
1534 self.reconfigureDevice(deviceClass, devid, dev_config)
1537 def pause(self):
1538 xc.domain_pause(self.domid)
1541 def unpause(self):
1542 xc.domain_unpause(self.domid)
1545 ## private:
1547 def restart(self, rename = False):
1548 """Restart the domain after it has exited.
1550 @param rename True if the old domain is to be renamed and preserved,
1551 False if it is to be destroyed.
1552 """
1554 self.configure_bootloader()
1555 config = self.sxpr()
1557 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1558 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1559 self.info['cpus'])])
1561 if self.readVm(RESTART_IN_PROGRESS):
1562 log.error('Xend failed during restart of domain %d. '
1563 'Refusing to restart to avoid loops.',
1564 self.domid)
1565 self.destroy()
1566 return
1568 self.writeVm(RESTART_IN_PROGRESS, 'True')
1570 now = time.time()
1571 rst = self.readVm('xend/previous_restart_time')
1572 if rst:
1573 rst = float(rst)
1574 timeout = now - rst
1575 if timeout < MINIMUM_RESTART_TIME:
1576 log.error(
1577 'VM %s restarting too fast (%f seconds since the last '
1578 'restart). Refusing to restart to avoid loops.',
1579 self.info['name'], timeout)
1580 self.destroy()
1581 return
1583 self.writeVm('xend/previous_restart_time', str(now))
1585 try:
1586 if rename:
1587 self.preserveForRestart()
1588 else:
1589 self.unwatchVm()
1590 self.destroyDomain()
1592 # new_dom's VM will be the same as this domain's VM, except where
1593 # the rename flag has instructed us to call preserveForRestart.
1594 # In that case, it is important that we remove the
1595 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1596 # once the new one is available.
1598 new_dom = None
1599 try:
1600 new_dom = XendDomain.instance().domain_create(config)
1601 new_dom.unpause()
1602 new_dom.removeVm(RESTART_IN_PROGRESS)
1603 except:
1604 if new_dom:
1605 new_dom.removeVm(RESTART_IN_PROGRESS)
1606 new_dom.destroy()
1607 else:
1608 self.removeVm(RESTART_IN_PROGRESS)
1609 raise
1610 except:
1611 log.exception('Failed to restart domain %d.', self.domid)
1614 def preserveForRestart(self):
1615 """Preserve a domain that has been shut down, by giving it a new UUID,
1616 cloning the VM details, and giving it a new name. This allows us to
1617 keep this domain for debugging, but restart a new one in its place
1618 preserving the restart semantics (name and UUID preserved).
1619 """
1621 new_name = self.generateUniqueName()
1622 new_uuid = uuid.toString(uuid.create())
1623 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1624 self.info['name'], self.domid, self.info['uuid'],
1625 new_name, new_uuid)
1626 self.unwatchVm()
1627 self.release_devices()
1628 self.info['name'] = new_name
1629 self.info['uuid'] = new_uuid
1630 self.vmpath = XendDomain.VMROOT + new_uuid
1631 self.storeVmDetails()
1632 self.preserve()
1635 def preserve(self):
1636 log.info("Preserving dead domain %s (%d).", self.info['name'],
1637 self.domid)
1638 self.unwatchVm()
1639 self.storeDom('xend/shutdown_completed', 'True')
1640 self.state_set(STATE_DOM_SHUTDOWN)
1643 # private:
1645 def generateUniqueName(self):
1646 n = 1
1647 while True:
1648 name = "%s-%d" % (self.info['name'], n)
1649 try:
1650 self.check_name(name)
1651 return name
1652 except VmError:
1653 n += 1
1656 def configure_bootloader(self):
1657 """Run the bootloader if we're configured to do so."""
1658 if not self.info['bootloader']:
1659 return
1660 blcfg = None
1661 # FIXME: this assumes that we want to use the first disk device
1662 for (n,c) in self.info['device']:
1663 if not n or not c or n != "vbd":
1664 continue
1665 disk = sxp.child_value(c, "uname")
1666 if disk is None:
1667 continue
1668 fn = blkdev_uname_to_file(disk)
1669 blcfg = bootloader(self.info['bootloader'], fn, 1,
1670 self.info['bootloader_args'],
1671 self.info['image'])
1672 break
1673 if blcfg is None:
1674 msg = "Had a bootloader specified, but can't find disk"
1675 log.error(msg)
1676 raise VmError(msg)
1677 self.info['image'] = blcfg
1680 def send_sysrq(self, key):
1681 asserts.isCharConvertible(key)
1683 self.storeDom("control/sysrq", '%c' % key)
1686 def infoIsSet(self, name):
1687 return name in self.info and self.info[name] is not None
1690 #============================================================================
1691 # Register device controllers and their device config types.
1693 """A map from device-class names to the subclass of DevController that
1694 implements the device control specific to that device-class."""
1695 controllerClasses = {}
1697 def addControllerClass(device_class, cls):
1698 """Register a subclass of DevController to handle the named device-class.
1699 """
1700 cls.deviceClass = device_class
1701 controllerClasses[device_class] = cls
1704 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1705 from xen.xend.server.BlktapController import BlktapController
1706 addControllerClass('vbd', blkif.BlkifController)
1707 addControllerClass('vif', netif.NetifController)
1708 addControllerClass('vtpm', tpmif.TPMifController)
1709 addControllerClass('pci', pciif.PciController)
1710 addControllerClass('ioports', iopif.IOPortsController)
1711 addControllerClass('irq', irqif.IRQController)
1712 addControllerClass('usb', usbif.UsbifController)
1713 addControllerClass('tap', BlktapController)