ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 10736:2937703f0ed0

Added blktap support. Includes kernel driver (enabled as CONFIG_XEN_BLKDEV_TAP=y) and userspace tools. The userspace deamon (blktapctrl) is enabled by default when xend is activated. For further information on using and configuring blktap see tools/blktap/README.
author jchesterfield@dhcp92.uk.xensource.com
date Thu Jul 13 10:13:26 2006 +0100 (2006-07-13)
parents b20580cf7fc1
children 08906834ca74
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] == 'ia64':
1288 # Workaround until ia64 properly supports ballooning.
1289 init_reservation = m
1291 xc.domain_memory_increase_reservation(self.domid, init_reservation,
1292 0, 0)
1294 self.createChannels()
1296 channel_details = self.image.createImage()
1298 self.store_mfn = channel_details['store_mfn']
1299 if 'console_mfn' in channel_details:
1300 self.console_mfn = channel_details['console_mfn']
1302 self.introduceDomain()
1304 self.createDevices()
1306 if self.info['bootloader']:
1307 self.image.cleanupBootloading()
1309 self.info['start_time'] = time.time()
1311 except RuntimeError, exn:
1312 raise VmError(str(exn))
1315 ## public:
1317 def cleanupDomain(self):
1318 """Cleanup domain resources; release devices. Idempotent. Nothrow
1319 guarantee."""
1321 self.refresh_shutdown_lock.acquire()
1322 try:
1323 self.unwatchShutdown()
1325 self.release_devices()
1327 if self.image:
1328 try:
1329 self.image.destroy()
1330 except:
1331 log.exception(
1332 "XendDomainInfo.cleanup: image.destroy() failed.")
1333 self.image = None
1335 try:
1336 self.removeDom()
1337 except:
1338 log.exception("Removing domain path failed.")
1340 try:
1341 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1342 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1343 except:
1344 log.exception("Renaming Zombie failed.")
1346 self.state_set(STATE_DOM_SHUTDOWN)
1347 finally:
1348 self.refresh_shutdown_lock.release()
1351 def cleanupVm(self):
1352 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1354 self.unwatchVm()
1356 try:
1357 self.removeVm()
1358 except:
1359 log.exception("Removing VM path failed.")
1362 ## private:
1364 def unwatchVm(self):
1365 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1366 guarantee."""
1368 try:
1369 try:
1370 if self.vmWatch:
1371 self.vmWatch.unwatch()
1372 finally:
1373 self.vmWatch = None
1374 except:
1375 log.exception("Unwatching VM path failed.")
1378 def unwatchShutdown(self):
1379 """Remove the watch on the domain's control/shutdown node, if any.
1380 Idempotent. Nothrow guarantee. Expects to be protected by the
1381 refresh_shutdown_lock."""
1383 try:
1384 try:
1385 if self.shutdownWatch:
1386 self.shutdownWatch.unwatch()
1387 finally:
1388 self.shutdownWatch = None
1389 except:
1390 log.exception("Unwatching control/shutdown failed.")
1393 ## public:
1395 def destroy(self):
1396 """Cleanup VM and destroy domain. Nothrow guarantee."""
1398 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1400 self.cleanupVm()
1401 if self.dompath is not None:
1402 self.destroyDomain()
1405 def destroyDomain(self):
1406 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1408 try:
1409 if self.domid is not None:
1410 xc.domain_destroy(self.domid)
1411 except:
1412 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1414 self.cleanupDomain()
1417 ## private:
1419 def release_devices(self):
1420 """Release all domain's devices. Nothrow guarantee."""
1422 while True:
1423 t = xstransact("%s/device" % self.dompath)
1424 for n in controllerClasses.keys():
1425 for d in t.list(n):
1426 try:
1427 t.remove(d)
1428 except:
1429 # Log and swallow any exceptions in removal --
1430 # there's nothing more we can do.
1431 log.exception(
1432 "Device release failed: %s; %s; %s",
1433 self.info['name'], n, d)
1434 if t.commit():
1435 break
1438 def createChannels(self):
1439 """Create the channels to the domain.
1440 """
1441 self.store_port = self.createChannel()
1442 self.console_port = self.createChannel()
1445 def createChannel(self):
1446 """Create an event channel to the domain.
1447 """
1448 try:
1449 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1450 except:
1451 log.exception("Exception in alloc_unbound(%d)", self.domid)
1452 raise
1455 ## public:
1457 def createDevices(self):
1458 """Create the devices for a vm.
1460 @raise: VmError for invalid devices
1461 """
1463 for (n, c) in self.info['device']:
1464 self.createDevice(n, c)
1466 if self.image:
1467 self.image.createDeviceModel()
1469 ## public:
1471 def testMigrateDevices(self, network, dst):
1472 """ Notify all device about intention of migration
1473 @raise: XendError for a device that cannot be migrated
1474 """
1475 for (n, c) in self.info['device']:
1476 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1477 if rc != 0:
1478 raise XendError("Device of type '%s' refuses migration." % n)
1480 def migrateDevices(self, network, dst, step, domName=''):
1481 """Notify the devices about migration
1482 """
1483 ctr = 0
1484 try:
1485 for (n, c) in self.info['device']:
1486 self.migrateDevice(n, c, network, dst, step, domName)
1487 ctr = ctr + 1
1488 except:
1489 for (n, c) in self.info['device']:
1490 if ctr == 0:
1491 step = step - 1
1492 ctr = ctr - 1
1493 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1494 raise
1496 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1497 step, domName=''):
1498 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1499 network, dst, step, domName)
1501 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1502 dst, step, domName=''):
1503 return self.getDeviceController(deviceClass).recover_migrate(
1504 deviceConfig, network, dst, step, domName)
1506 def waitForDevices(self):
1507 """Wait for this domain's configured devices to connect.
1509 @raise: VmError if any device fails to initialise.
1510 """
1511 for c in controllerClasses:
1512 self.waitForDevices_(c)
1515 def device_create(self, dev_config):
1516 """Create a new device.
1518 @param dev_config: device configuration
1519 """
1520 dev_type = sxp.name(dev_config)
1521 devid = self.createDevice(dev_type, dev_config)
1522 self.waitForDevice(dev_type, devid)
1523 self.info['device'].append((dev_type, dev_config))
1524 return self.getDeviceController(dev_type).sxpr(devid)
1527 def device_configure(self, dev_config, devid):
1528 """Configure an existing device.
1529 @param dev_config: device configuration
1530 @param devid: device id
1531 """
1532 deviceClass = sxp.name(dev_config)
1533 self.reconfigureDevice(deviceClass, devid, dev_config)
1536 def pause(self):
1537 xc.domain_pause(self.domid)
1540 def unpause(self):
1541 xc.domain_unpause(self.domid)
1544 ## private:
1546 def restart(self, rename = False):
1547 """Restart the domain after it has exited.
1549 @param rename True if the old domain is to be renamed and preserved,
1550 False if it is to be destroyed.
1551 """
1553 self.configure_bootloader()
1554 config = self.sxpr()
1556 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1557 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1558 self.info['cpus'])])
1560 if self.readVm(RESTART_IN_PROGRESS):
1561 log.error('Xend failed during restart of domain %d. '
1562 'Refusing to restart to avoid loops.',
1563 self.domid)
1564 self.destroy()
1565 return
1567 self.writeVm(RESTART_IN_PROGRESS, 'True')
1569 now = time.time()
1570 rst = self.readVm('xend/previous_restart_time')
1571 if rst:
1572 rst = float(rst)
1573 timeout = now - rst
1574 if timeout < MINIMUM_RESTART_TIME:
1575 log.error(
1576 'VM %s restarting too fast (%f seconds since the last '
1577 'restart). Refusing to restart to avoid loops.',
1578 self.info['name'], timeout)
1579 self.destroy()
1580 return
1582 self.writeVm('xend/previous_restart_time', str(now))
1584 try:
1585 if rename:
1586 self.preserveForRestart()
1587 else:
1588 self.unwatchVm()
1589 self.destroyDomain()
1591 # new_dom's VM will be the same as this domain's VM, except where
1592 # the rename flag has instructed us to call preserveForRestart.
1593 # In that case, it is important that we remove the
1594 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1595 # once the new one is available.
1597 new_dom = None
1598 try:
1599 new_dom = XendDomain.instance().domain_create(config)
1600 new_dom.unpause()
1601 new_dom.removeVm(RESTART_IN_PROGRESS)
1602 except:
1603 if new_dom:
1604 new_dom.removeVm(RESTART_IN_PROGRESS)
1605 new_dom.destroy()
1606 else:
1607 self.removeVm(RESTART_IN_PROGRESS)
1608 raise
1609 except:
1610 log.exception('Failed to restart domain %d.', self.domid)
1613 def preserveForRestart(self):
1614 """Preserve a domain that has been shut down, by giving it a new UUID,
1615 cloning the VM details, and giving it a new name. This allows us to
1616 keep this domain for debugging, but restart a new one in its place
1617 preserving the restart semantics (name and UUID preserved).
1618 """
1620 new_name = self.generateUniqueName()
1621 new_uuid = uuid.toString(uuid.create())
1622 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1623 self.info['name'], self.domid, self.info['uuid'],
1624 new_name, new_uuid)
1625 self.unwatchVm()
1626 self.release_devices()
1627 self.info['name'] = new_name
1628 self.info['uuid'] = new_uuid
1629 self.vmpath = XendDomain.VMROOT + new_uuid
1630 self.storeVmDetails()
1631 self.preserve()
1634 def preserve(self):
1635 log.info("Preserving dead domain %s (%d).", self.info['name'],
1636 self.domid)
1637 self.unwatchVm()
1638 self.storeDom('xend/shutdown_completed', 'True')
1639 self.state_set(STATE_DOM_SHUTDOWN)
1642 # private:
1644 def generateUniqueName(self):
1645 n = 1
1646 while True:
1647 name = "%s-%d" % (self.info['name'], n)
1648 try:
1649 self.check_name(name)
1650 return name
1651 except VmError:
1652 n += 1
1655 def configure_bootloader(self):
1656 """Run the bootloader if we're configured to do so."""
1657 if not self.info['bootloader']:
1658 return
1659 blcfg = None
1660 # FIXME: this assumes that we want to use the first disk device
1661 for (n,c) in self.info['device']:
1662 if not n or not c or n != "vbd":
1663 continue
1664 disk = sxp.child_value(c, "uname")
1665 if disk is None:
1666 continue
1667 fn = blkdev_uname_to_file(disk)
1668 blcfg = bootloader(self.info['bootloader'], fn, 1,
1669 self.info['bootloader_args'],
1670 self.info['image'])
1671 break
1672 if blcfg is None:
1673 msg = "Had a bootloader specified, but can't find disk"
1674 log.error(msg)
1675 raise VmError(msg)
1676 self.info['image'] = blcfg
1679 def send_sysrq(self, key):
1680 asserts.isCharConvertible(key)
1682 self.storeDom("control/sysrq", '%c' % key)
1685 def infoIsSet(self, name):
1686 return name in self.info and self.info[name] is not None
1689 #============================================================================
1690 # Register device controllers and their device config types.
1692 """A map from device-class names to the subclass of DevController that
1693 implements the device control specific to that device-class."""
1694 controllerClasses = {}
1696 def addControllerClass(device_class, cls):
1697 """Register a subclass of DevController to handle the named device-class.
1698 """
1699 cls.deviceClass = device_class
1700 controllerClasses[device_class] = cls
1703 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1704 from xen.xend.server.BlktapController import BlktapController
1705 addControllerClass('vbd', blkif.BlkifController)
1706 addControllerClass('vif', netif.NetifController)
1707 addControllerClass('vtpm', tpmif.TPMifController)
1708 addControllerClass('pci', pciif.PciController)
1709 addControllerClass('ioports', iopif.IOPortsController)
1710 addControllerClass('irq', irqif.IRQController)
1711 addControllerClass('usb', usbif.UsbifController)
1712 addControllerClass('tap', BlktapController)