ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 10716:3c74df4f33f0

[XEND] Currently a domain's maxmem value is being set with the memory value
even if the user specifies maxmem in config. This patch uses maxmem
value to set maxmem instead of memory.

Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
author kfraser@localhost.localdomain
date Mon Jul 10 15:57:56 2006 +0100 (2006-07-10)
parents 800261a88275
children b20580cf7fc1
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 domain_by_uuid(uuid):
400 return XendDomain.instance().domain_lookup_by_uuid_nr(uuid)
403 def shutdown_reason(code):
404 """Get a shutdown reason from a code.
406 @param code: shutdown code
407 @type code: int
408 @return: shutdown reason
409 @rtype: string
410 """
411 return shutdown_reasons.get(code, "?")
413 def dom_get(dom):
414 """Get info from xen for an existing domain.
416 @param dom: domain id
417 @return: info or None
418 """
419 try:
420 domlist = xc.domain_getinfo(dom, 1)
421 if domlist and dom == domlist[0]['dom']:
422 return domlist[0]
423 except Exception, err:
424 # ignore missing domain
425 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
426 return None
429 class XendDomainInfo:
431 def __init__(self, info, domid = None, dompath = None, augment = False,
432 priv = False, resume = False):
434 self.info = info
436 if not self.infoIsSet('uuid'):
437 self.info['uuid'] = uuid.toString(uuid.create())
439 if domid is not None:
440 self.domid = domid
441 elif 'dom' in info:
442 self.domid = int(info['dom'])
443 else:
444 self.domid = None
446 self.vmpath = XendDomain.VMROOT + self.info['uuid']
447 self.dompath = dompath
449 if augment:
450 self.augmentInfo(priv)
452 self.validateInfo()
454 self.image = None
455 self.security = None
456 self.store_port = None
457 self.store_mfn = None
458 self.console_port = None
459 self.console_mfn = None
461 self.vmWatch = None
462 self.shutdownWatch = None
464 self.shutdownStartTime = None
466 self.state = STATE_DOM_OK
467 self.state_updated = threading.Condition()
468 self.refresh_shutdown_lock = threading.Condition()
470 self.setResume(resume)
472 ## private:
474 def readVMDetails(self, params):
475 """Read the specified parameters from the store.
476 """
477 try:
478 return self.gatherVm(*params)
479 except ValueError:
480 # One of the int/float entries in params has a corresponding store
481 # entry that is invalid. We recover, because older versions of
482 # Xend may have put the entry there (memory/target, for example),
483 # but this is in general a bad situation to have reached.
484 log.exception(
485 "Store corrupted at %s! Domain %d's configuration may be "
486 "affected.", self.vmpath, self.domid)
487 return []
490 def storeChanged(self, _):
491 log.trace("XendDomainInfo.storeChanged");
493 changed = False
495 def f(x, y):
496 if y is not None and self.info[x[0]] != y:
497 self.info[x[0]] = y
498 changed = True
500 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
502 im = self.readVm('image')
503 current_im = self.info['image']
504 if (im is not None and
505 (current_im is None or sxp.to_string(current_im) != im)):
506 self.info['image'] = sxp.from_string(im)
507 changed = True
509 if changed:
510 # Update the domain section of the store, as this contains some
511 # parameters derived from the VM configuration.
512 self.storeDomDetails()
514 return 1
517 def augmentInfo(self, priv):
518 """Augment self.info, as given to us through {@link #recreate}, with
519 values taken from the store. This recovers those values known to xend
520 but not to the hypervisor.
521 """
522 def useIfNeeded(name, val):
523 if not self.infoIsSet(name) and val is not None:
524 self.info[name] = val
526 if priv:
527 entries = VM_STORE_ENTRIES[:]
528 entries.remove(('memory', int))
529 entries.remove(('maxmem', int))
530 else:
531 entries = VM_STORE_ENTRIES
532 entries.append(('image', str))
533 entries.append(('security', str))
535 map(lambda x, y: useIfNeeded(x[0], y), entries,
536 self.readVMDetails(entries))
538 device = []
539 for c in controllerClasses:
540 devconfig = self.getDeviceConfigurations(c)
541 if devconfig:
542 device.extend(map(lambda x: (c, x), devconfig))
543 useIfNeeded('device', device)
546 def validateInfo(self):
547 """Validate and normalise the info block. This has either been parsed
548 by parseConfig, or received from xc through recreate and augmented by
549 the current store contents.
550 """
551 def defaultInfo(name, val):
552 if not self.infoIsSet(name):
553 self.info[name] = val()
555 try:
556 defaultInfo('name', lambda: "Domain-%d" % self.domid)
557 defaultInfo('on_poweroff', lambda: "destroy")
558 defaultInfo('on_reboot', lambda: "restart")
559 defaultInfo('on_crash', lambda: "restart")
560 defaultInfo('features', lambda: "")
561 defaultInfo('cpu', lambda: None)
562 defaultInfo('cpus', lambda: [])
563 defaultInfo('cpu_weight', lambda: 1.0)
565 # some domains don't have a config file (e.g. dom0 )
566 # to set number of vcpus so we derive available cpus
567 # from max_vcpu_id which is present for running domains.
568 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
569 avail = int(self.info['max_vcpu_id'])+1
570 else:
571 avail = int(1)
573 defaultInfo('vcpus', lambda: avail)
574 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
575 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
576 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
578 defaultInfo('memory', lambda: 0)
579 defaultInfo('maxmem', lambda: 0)
580 defaultInfo('bootloader', lambda: None)
581 defaultInfo('bootloader_args', lambda: None)
582 defaultInfo('backend', lambda: [])
583 defaultInfo('device', lambda: [])
584 defaultInfo('image', lambda: None)
585 defaultInfo('security', lambda: None)
587 self.check_name(self.info['name'])
588 self.check_uuid(self.info['uuid'])
590 if isinstance(self.info['image'], str):
591 self.info['image'] = sxp.from_string(self.info['image'])
593 if isinstance(self.info['security'], str):
594 self.info['security'] = sxp.from_string(self.info['security'])
596 if self.info['memory'] == 0:
597 if self.infoIsSet('mem_kb'):
598 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
600 if self.info['maxmem'] < self.info['memory']:
601 self.info['maxmem'] = self.info['memory']
603 for (n, c) in self.info['device']:
604 if not n or not c or n not in controllerClasses:
605 raise VmError('invalid device (%s, %s)' %
606 (str(n), str(c)))
608 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
609 if self.info[event] not in restart_modes:
610 raise VmError('invalid restart event: %s = %s' %
611 (event, str(self.info[event])))
613 except KeyError, exn:
614 log.exception(exn)
615 raise VmError('Unspecified domain detail: %s' % exn)
618 def readVm(self, *args):
619 return xstransact.Read(self.vmpath, *args)
621 def writeVm(self, *args):
622 return xstransact.Write(self.vmpath, *args)
624 def removeVm(self, *args):
625 return xstransact.Remove(self.vmpath, *args)
627 def gatherVm(self, *args):
628 return xstransact.Gather(self.vmpath, *args)
631 ## public:
633 def storeVm(self, *args):
634 return xstransact.Store(self.vmpath, *args)
637 ## private:
639 def readDom(self, *args):
640 return xstransact.Read(self.dompath, *args)
642 def writeDom(self, *args):
643 return xstransact.Write(self.dompath, *args)
646 ## public:
648 def removeDom(self, *args):
649 return xstransact.Remove(self.dompath, *args)
651 def recreateDom(self):
652 complete(self.dompath, lambda t: self._recreateDom(t))
654 def _recreateDom(self, t):
655 t.remove()
656 t.mkdir()
657 t.set_permissions({ 'dom' : self.domid })
660 ## private:
662 def storeDom(self, *args):
663 return xstransact.Store(self.dompath, *args)
666 ## public:
668 def completeRestore(self, store_mfn, console_mfn):
670 log.debug("XendDomainInfo.completeRestore")
672 self.store_mfn = store_mfn
673 self.console_mfn = console_mfn
675 self.introduceDomain()
676 self.storeDomDetails()
677 self.registerWatches()
678 self.refreshShutdown()
680 log.debug("XendDomainInfo.completeRestore done")
683 def storeVmDetails(self):
684 to_store = {}
686 for k in VM_STORE_ENTRIES:
687 if self.infoIsSet(k[0]):
688 to_store[k[0]] = str(self.info[k[0]])
690 if self.infoIsSet('image'):
691 to_store['image'] = sxp.to_string(self.info['image'])
693 if self.infoIsSet('security'):
694 security = self.info['security']
695 to_store['security'] = sxp.to_string(security)
696 for idx in range(0, len(security)):
697 if security[idx][0] == 'access_control':
698 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
699 for aidx in range(1, len(security[idx])):
700 if security[idx][aidx][0] == 'label':
701 to_store['security/access_control/label'] = security[idx][aidx][1]
702 if security[idx][aidx][0] == 'policy':
703 to_store['security/access_control/policy'] = security[idx][aidx][1]
704 if security[idx][0] == 'ssidref':
705 to_store['security/ssidref'] = str(security[idx][1])
707 log.debug("Storing VM details: %s", to_store)
709 self.writeVm(to_store)
710 self.setVmPermissions()
713 def setVmPermissions(self):
714 """Allow the guest domain to read its UUID. We don't allow it to
715 access any other entry, for security."""
716 xstransact.SetPermissions('%s/uuid' % self.vmpath,
717 { 'dom' : self.domid,
718 'read' : True,
719 'write' : False })
722 def storeDomDetails(self):
723 to_store = {
724 'domid': str(self.domid),
725 'vm': self.vmpath,
726 'name': self.info['name'],
727 'console/limit': str(xroot.get_console_limit() * 1024),
728 'memory/target': str(self.info['memory'] * 1024)
729 }
731 def f(n, v):
732 if v is not None:
733 to_store[n] = str(v)
735 f('console/port', self.console_port)
736 f('console/ring-ref', self.console_mfn)
737 f('store/port', self.store_port)
738 f('store/ring-ref', self.store_mfn)
740 to_store.update(self.vcpuDomDetails())
742 log.debug("Storing domain details: %s", to_store)
744 self.writeDom(to_store)
747 ## private:
749 def vcpuDomDetails(self):
750 def availability(n):
751 if self.info['vcpu_avail'] & (1 << n):
752 return 'online'
753 else:
754 return 'offline'
756 result = {}
757 for v in range(0, self.info['vcpus']):
758 result["cpu/%d/availability" % v] = availability(v)
759 return result
762 ## public:
764 def registerWatches(self):
765 """Register a watch on this VM's entries in the store, and the
766 domain's control/shutdown node, so that when they are changed
767 externally, we keep up to date. This should only be called by {@link
768 #create}, {@link #recreate}, or {@link #restore}, once the domain's
769 details have been written, but before the new instance is returned."""
770 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
771 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
772 self.handleShutdownWatch)
775 def getDomid(self):
776 return self.domid
778 def setName(self, name):
779 self.check_name(name)
780 self.info['name'] = name
781 self.storeVm("name", name)
783 def getName(self):
784 return self.info['name']
786 def getUuid(self):
787 return self.info['uuid']
789 def getDomainPath(self):
790 return self.dompath
793 def getStorePort(self):
794 """For use only by image.py and XendCheckpoint.py."""
795 return self.store_port
798 def getConsolePort(self):
799 """For use only by image.py and XendCheckpoint.py"""
800 return self.console_port
802 def getFeatures(self):
803 """For use only by image.py."""
804 return self.info['features']
806 def getVCpuCount(self):
807 return self.info['vcpus']
810 def setVCpuCount(self, vcpus):
811 self.info['vcpu_avail'] = (1 << vcpus) - 1
812 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
813 self.writeDom(self.vcpuDomDetails())
815 def getLabel(self):
816 return security.get_security_info(self.info, 'label')
818 def getMemoryTarget(self):
819 """Get this domain's target memory size, in KB."""
820 return self.info['memory'] * 1024
822 def getResume(self):
823 return "%s" % self.info['resume']
825 def endRestore(self):
826 self.setResume(False)
828 def setResume(self, state):
829 self.info['resume'] = state
831 def refreshShutdown(self, xeninfo = None):
832 # If set at the end of this method, a restart is required, with the
833 # given reason. This restart has to be done out of the scope of
834 # refresh_shutdown_lock.
835 restart_reason = None
837 self.refresh_shutdown_lock.acquire()
838 try:
839 if xeninfo is None:
840 xeninfo = dom_get(self.domid)
841 if xeninfo is None:
842 # The domain no longer exists. This will occur if we have
843 # scheduled a timer to check for shutdown timeouts and the
844 # shutdown succeeded. It will also occur if someone
845 # destroys a domain beneath us. We clean up the domain,
846 # just in case, but we can't clean up the VM, because that
847 # VM may have migrated to a different domain on this
848 # machine.
849 self.cleanupDomain()
850 return
852 if xeninfo['dying']:
853 # Dying means that a domain has been destroyed, but has not
854 # yet been cleaned up by Xen. This state could persist
855 # indefinitely if, for example, another domain has some of its
856 # pages mapped. We might like to diagnose this problem in the
857 # future, but for now all we do is make sure that it's not us
858 # holding the pages, by calling cleanupDomain. We can't
859 # clean up the VM, as above.
860 self.cleanupDomain()
861 return
863 elif xeninfo['crashed']:
864 if self.readDom('xend/shutdown_completed'):
865 # We've seen this shutdown already, but we are preserving
866 # the domain for debugging. Leave it alone.
867 return
869 log.warn('Domain has crashed: name=%s id=%d.',
870 self.info['name'], self.domid)
872 if xroot.get_enable_dump():
873 self.dumpCore()
875 restart_reason = 'crash'
877 elif xeninfo['shutdown']:
878 if self.readDom('xend/shutdown_completed'):
879 # We've seen this shutdown already, but we are preserving
880 # the domain for debugging. Leave it alone.
881 return
883 else:
884 reason = shutdown_reason(xeninfo['shutdown_reason'])
886 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
887 self.info['name'], self.domid, reason)
889 self.clearRestart()
891 if reason == 'suspend':
892 self.state_set(STATE_DOM_SHUTDOWN)
893 # Don't destroy the domain. XendCheckpoint will do
894 # this once it has finished. However, stop watching
895 # the VM path now, otherwise we will end up with one
896 # watch for the old domain, and one for the new.
897 self.unwatchVm()
898 elif reason in ['poweroff', 'reboot']:
899 restart_reason = reason
900 else:
901 self.destroy()
903 elif self.dompath is None:
904 # We have yet to manage to call introduceDomain on this
905 # domain. This can happen if a restore is in progress, or has
906 # failed. Ignore this domain.
907 pass
908 else:
909 # Domain is alive. If we are shutting it down, then check
910 # the timeout on that, and destroy it if necessary.
912 if self.shutdownStartTime:
913 timeout = (SHUTDOWN_TIMEOUT - time.time() +
914 self.shutdownStartTime)
915 if timeout < 0:
916 log.info(
917 "Domain shutdown timeout expired: name=%s id=%s",
918 self.info['name'], self.domid)
919 self.destroy()
920 finally:
921 self.refresh_shutdown_lock.release()
923 if restart_reason:
924 self.maybeRestart(restart_reason)
927 def handleShutdownWatch(self, _):
928 log.debug('XendDomainInfo.handleShutdownWatch')
930 reason = self.readDom('control/shutdown')
932 if reason and reason != 'suspend':
933 sst = self.readDom('xend/shutdown_start_time')
934 now = time.time()
935 if sst:
936 self.shutdownStartTime = float(sst)
937 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
938 else:
939 self.shutdownStartTime = now
940 self.storeDom('xend/shutdown_start_time', now)
941 timeout = SHUTDOWN_TIMEOUT
943 log.trace(
944 "Scheduling refreshShutdown on domain %d in %ds.",
945 self.domid, timeout)
946 threading.Timer(timeout, self.refreshShutdown).start()
948 return True
951 def shutdown(self, reason):
952 if not reason in shutdown_reasons.values():
953 raise XendError('Invalid reason: %s' % reason)
954 self.storeDom("control/shutdown", reason)
957 ## private:
959 def clearRestart(self):
960 self.removeDom("xend/shutdown_start_time")
963 def maybeRestart(self, reason):
964 # Dispatch to the correct method based upon the configured on_{reason}
965 # behaviour.
966 {"destroy" : self.destroy,
967 "restart" : self.restart,
968 "preserve" : self.preserve,
969 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
972 def renameRestart(self):
973 self.restart(True)
976 def dumpCore(self):
977 """Create a core dump for this domain. Nothrow guarantee."""
979 try:
980 corefile = "/var/xen/dump/%s.%s.core" % (self.info['name'],
981 self.domid)
982 xc.domain_dumpcore(self.domid, corefile)
984 except:
985 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
986 self.domid, self.info['name'])
989 ## public:
991 def setMemoryTarget(self, target):
992 """Set the memory target of this domain.
993 @param target In MiB.
994 """
995 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
996 self.info['name'], self.domid, target)
998 self.info['memory'] = target
999 self.storeVm("memory", target)
1000 self.storeDom("memory/target", target << 10)
1003 def update(self, info = None):
1004 """Update with info from xc.domain_getinfo().
1005 """
1007 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1008 if not info:
1009 info = dom_get(self.domid)
1010 if not info:
1011 return
1013 #manually update ssidref / security fields
1014 if security.on() and info.has_key('ssidref'):
1015 if (info['ssidref'] != 0) and self.info.has_key('security'):
1016 security_field = self.info['security']
1017 if not security_field:
1018 #create new security element
1019 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1020 #ssidref field not used any longer
1021 info.pop('ssidref')
1023 self.info.update(info)
1024 self.validateInfo()
1025 self.refreshShutdown(info)
1027 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1028 self.info)
1031 ## private:
1033 def state_set(self, state):
1034 self.state_updated.acquire()
1035 try:
1036 if self.state != state:
1037 self.state = state
1038 self.state_updated.notifyAll()
1039 finally:
1040 self.state_updated.release()
1043 ## public:
1045 def waitForShutdown(self):
1046 self.state_updated.acquire()
1047 try:
1048 while self.state == STATE_DOM_OK:
1049 self.state_updated.wait()
1050 finally:
1051 self.state_updated.release()
1054 def __str__(self):
1055 s = "<domain"
1056 s += " id=" + str(self.domid)
1057 s += " name=" + self.info['name']
1058 s += " memory=" + str(self.info['memory'])
1059 s += ">"
1060 return s
1062 __repr__ = __str__
1065 ## private:
1067 def createDevice(self, deviceClass, devconfig):
1068 return self.getDeviceController(deviceClass).createDevice(devconfig)
1071 def waitForDevices_(self, deviceClass):
1072 return self.getDeviceController(deviceClass).waitForDevices()
1075 def waitForDevice(self, deviceClass, devid):
1076 return self.getDeviceController(deviceClass).waitForDevice(devid)
1079 def reconfigureDevice(self, deviceClass, devid, devconfig):
1080 return self.getDeviceController(deviceClass).reconfigureDevice(
1081 devid, devconfig)
1084 ## public:
1086 def destroyDevice(self, deviceClass, devid):
1087 return self.getDeviceController(deviceClass).destroyDevice(devid)
1090 def getDeviceSxprs(self, deviceClass):
1091 return self.getDeviceController(deviceClass).sxprs()
1094 ## private:
1096 def getDeviceConfigurations(self, deviceClass):
1097 return self.getDeviceController(deviceClass).configurations()
1100 def getDeviceController(self, name):
1101 if name not in controllerClasses:
1102 raise XendError("unknown device type: " + str(name))
1104 return controllerClasses[name](self)
1107 ## public:
1109 def sxpr(self):
1110 sxpr = ['domain',
1111 ['domid', self.domid]]
1113 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1114 if self.infoIsSet(e[0]):
1115 sxpr.append([e[0], self.info[e[0]]])
1117 if self.infoIsSet('image'):
1118 sxpr.append(['image', self.info['image']])
1120 if self.infoIsSet('security'):
1121 sxpr.append(['security', self.info['security']])
1123 for cls in controllerClasses:
1124 for config in self.getDeviceConfigurations(cls):
1125 sxpr.append(['device', config])
1127 def stateChar(name):
1128 if name in self.info:
1129 if self.info[name]:
1130 return name[0]
1131 else:
1132 return '-'
1133 else:
1134 return '?'
1136 state = reduce(
1137 lambda x, y: x + y,
1138 map(stateChar,
1139 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1140 'dying']))
1142 sxpr.append(['state', state])
1143 if self.infoIsSet('shutdown'):
1144 reason = shutdown_reason(self.info['shutdown_reason'])
1145 sxpr.append(['shutdown_reason', reason])
1146 if self.infoIsSet('cpu_time'):
1147 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1148 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1150 if self.infoIsSet('start_time'):
1151 up_time = time.time() - self.info['start_time']
1152 sxpr.append(['up_time', str(up_time) ])
1153 sxpr.append(['start_time', str(self.info['start_time']) ])
1155 if self.store_mfn:
1156 sxpr.append(['store_mfn', self.store_mfn])
1157 if self.console_mfn:
1158 sxpr.append(['console_mfn', self.console_mfn])
1160 return sxpr
1163 def getVCPUInfo(self):
1164 try:
1165 # We include the domain name and ID, to help xm.
1166 sxpr = ['domain',
1167 ['domid', self.domid],
1168 ['name', self.info['name']],
1169 ['vcpu_count', self.info['online_vcpus']]]
1171 for i in range(0, self.info['max_vcpu_id']+1):
1172 info = xc.vcpu_getinfo(self.domid, i)
1174 sxpr.append(['vcpu',
1175 ['number', i],
1176 ['online', info['online']],
1177 ['blocked', info['blocked']],
1178 ['running', info['running']],
1179 ['cpu_time', info['cpu_time'] / 1e9],
1180 ['cpu', info['cpu']],
1181 ['cpumap', info['cpumap']]])
1183 return sxpr
1185 except RuntimeError, exn:
1186 raise XendError(str(exn))
1189 ## private:
1191 def check_name(self, name):
1192 """Check if a vm name is valid. Valid names contain alphabetic characters,
1193 digits, or characters in '_-.:/+'.
1194 The same name cannot be used for more than one vm at the same time.
1196 @param name: name
1197 @raise: VmError if invalid
1198 """
1199 if name is None or name == '':
1200 raise VmError('missing vm name')
1201 for c in name:
1202 if c in string.digits: continue
1203 if c in '_-.:/+': continue
1204 if c in string.ascii_letters: continue
1205 raise VmError('invalid vm name')
1207 dominfo = domain_by_name(name)
1208 if not dominfo:
1209 return
1210 if self.domid is None:
1211 raise VmError("VM name '%s' already in use by domain %d" %
1212 (name, dominfo.domid))
1213 if dominfo.domid != self.domid:
1214 raise VmError("VM name '%s' is used in both domains %d and %d" %
1215 (name, self.domid, dominfo.domid))
1218 def check_uuid(self, uuid):
1219 """The same uuid cannot be used for more than one vm at the same time.
1221 @param uuid: uuid
1222 @raise: VmError if same uuid is used
1223 """
1224 dominfo = domain_by_uuid(uuid)
1225 if not dominfo:
1226 return
1227 if self.domid is None:
1228 raise VmError("uuid '%s' already in use by domain %d" %
1229 (uuid, dominfo.domid))
1230 if dominfo.domid != self.domid:
1231 raise VmError("uuid '%s' is used in both domains %d and %d" %
1232 (uuid, self.domid, dominfo.domid))
1235 def construct(self):
1236 """Construct the domain.
1238 @raise: VmError on error
1239 """
1241 log.debug('XendDomainInfo.construct: %s',
1242 self.domid)
1244 self.domid = xc.domain_create(
1245 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1246 handle = uuid.fromString(self.info['uuid']))
1248 if self.domid < 0:
1249 raise VmError('Creating domain failed: name=%s' %
1250 self.info['name'])
1252 self.dompath = GetDomainPath(self.domid)
1254 self.recreateDom()
1256 # Set maximum number of vcpus in domain
1257 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1260 def introduceDomain(self):
1261 assert self.domid is not None
1262 assert self.store_mfn is not None
1263 assert self.store_port is not None
1265 try:
1266 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1267 except RuntimeError, exn:
1268 raise XendError(str(exn))
1271 def initDomain(self):
1272 log.debug('XendDomainInfo.initDomain: %s %s',
1273 self.domid,
1274 self.info['cpu_weight'])
1276 # if we have a boot loader but no image, then we need to set things
1277 # up by running the boot loader non-interactively
1278 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1279 self.configure_bootloader()
1281 if not self.infoIsSet('image'):
1282 raise VmError('Missing image in configuration')
1284 try:
1285 self.image = image.create(self,
1286 self.info['image'],
1287 self.info['device'])
1289 localtime = self.info['localtime']
1290 if localtime is not None and localtime == 1:
1291 xc.domain_set_time_offset(self.domid)
1293 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1295 # repin domain vcpus if a restricted cpus list is provided
1296 # this is done prior to memory allocation to aide in memory
1297 # distribution for NUMA systems.
1298 cpus = self.info['cpus']
1299 if cpus is not None and len(cpus) > 0:
1300 for v in range(0, self.info['max_vcpu_id']+1):
1301 # pincpu takes a list of ints
1302 cpu = [ int( cpus[v % len(cpus)] ) ]
1303 xc.vcpu_setaffinity(self.domid, v, cpu)
1305 # set domain maxmem in KiB
1306 xc.domain_setmaxmem(self.domid, self.info['maxmem'] * 1024)
1308 m = self.image.getDomainMemory(self.info['memory'] * 1024)
1309 balloon.free(m)
1311 init_reservation = self.info['memory'] * 1024
1312 if os.uname()[4] == 'ia64':
1313 # Workaround until ia64 properly supports ballooning.
1314 init_reservation = m
1316 xc.domain_memory_increase_reservation(self.domid, init_reservation,
1317 0, 0)
1319 self.createChannels()
1321 channel_details = self.image.createImage()
1323 self.store_mfn = channel_details['store_mfn']
1324 if 'console_mfn' in channel_details:
1325 self.console_mfn = channel_details['console_mfn']
1327 self.introduceDomain()
1329 self.createDevices()
1331 if self.info['bootloader']:
1332 self.image.cleanupBootloading()
1334 self.info['start_time'] = time.time()
1336 except RuntimeError, exn:
1337 raise VmError(str(exn))
1340 ## public:
1342 def cleanupDomain(self):
1343 """Cleanup domain resources; release devices. Idempotent. Nothrow
1344 guarantee."""
1346 self.refresh_shutdown_lock.acquire()
1347 try:
1348 self.unwatchShutdown()
1350 self.release_devices()
1352 if self.image:
1353 try:
1354 self.image.destroy()
1355 except:
1356 log.exception(
1357 "XendDomainInfo.cleanup: image.destroy() failed.")
1358 self.image = None
1360 try:
1361 self.removeDom()
1362 except:
1363 log.exception("Removing domain path failed.")
1365 try:
1366 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1367 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1368 except:
1369 log.exception("Renaming Zombie failed.")
1371 self.state_set(STATE_DOM_SHUTDOWN)
1372 finally:
1373 self.refresh_shutdown_lock.release()
1376 def cleanupVm(self):
1377 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1379 self.unwatchVm()
1381 try:
1382 self.removeVm()
1383 except:
1384 log.exception("Removing VM path failed.")
1387 ## private:
1389 def unwatchVm(self):
1390 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1391 guarantee."""
1393 try:
1394 try:
1395 if self.vmWatch:
1396 self.vmWatch.unwatch()
1397 finally:
1398 self.vmWatch = None
1399 except:
1400 log.exception("Unwatching VM path failed.")
1403 def unwatchShutdown(self):
1404 """Remove the watch on the domain's control/shutdown node, if any.
1405 Idempotent. Nothrow guarantee. Expects to be protected by the
1406 refresh_shutdown_lock."""
1408 try:
1409 try:
1410 if self.shutdownWatch:
1411 self.shutdownWatch.unwatch()
1412 finally:
1413 self.shutdownWatch = None
1414 except:
1415 log.exception("Unwatching control/shutdown failed.")
1418 ## public:
1420 def destroy(self):
1421 """Cleanup VM and destroy domain. Nothrow guarantee."""
1423 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1425 self.cleanupVm()
1426 if self.dompath is not None:
1427 self.destroyDomain()
1430 def destroyDomain(self):
1431 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1433 try:
1434 if self.domid is not None:
1435 xc.domain_destroy(self.domid)
1436 except:
1437 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1439 self.cleanupDomain()
1442 ## private:
1444 def release_devices(self):
1445 """Release all domain's devices. Nothrow guarantee."""
1447 while True:
1448 t = xstransact("%s/device" % self.dompath)
1449 for n in controllerClasses.keys():
1450 for d in t.list(n):
1451 try:
1452 t.remove(d)
1453 except:
1454 # Log and swallow any exceptions in removal --
1455 # there's nothing more we can do.
1456 log.exception(
1457 "Device release failed: %s; %s; %s",
1458 self.info['name'], n, d)
1459 if t.commit():
1460 break
1463 def createChannels(self):
1464 """Create the channels to the domain.
1465 """
1466 self.store_port = self.createChannel()
1467 self.console_port = self.createChannel()
1470 def createChannel(self):
1471 """Create an event channel to the domain.
1472 """
1473 try:
1474 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1475 except:
1476 log.exception("Exception in alloc_unbound(%d)", self.domid)
1477 raise
1480 ## public:
1482 def createDevices(self):
1483 """Create the devices for a vm.
1485 @raise: VmError for invalid devices
1486 """
1488 for (n, c) in self.info['device']:
1489 self.createDevice(n, c)
1491 if self.image:
1492 self.image.createDeviceModel()
1494 ## public:
1496 def testMigrateDevices(self, network, dst):
1497 """ Notify all device about intention of migration
1498 @raise: XendError for a device that cannot be migrated
1499 """
1500 for (n, c) in self.info['device']:
1501 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1502 if rc != 0:
1503 raise XendError("Device of type '%s' refuses migration." % n)
1505 def migrateDevices(self, network, dst, step, domName=''):
1506 """Notify the devices about migration
1507 """
1508 ctr = 0
1509 try:
1510 for (n, c) in self.info['device']:
1511 self.migrateDevice(n, c, network, dst, step, domName)
1512 ctr = ctr + 1
1513 except:
1514 for (n, c) in self.info['device']:
1515 if ctr == 0:
1516 step = step - 1
1517 ctr = ctr - 1
1518 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1519 raise
1521 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1522 step, domName=''):
1523 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1524 network, dst, step, domName)
1526 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1527 dst, step, domName=''):
1528 return self.getDeviceController(deviceClass).recover_migrate(
1529 deviceConfig, network, dst, step, domName)
1531 def waitForDevices(self):
1532 """Wait for this domain's configured devices to connect.
1534 @raise: VmError if any device fails to initialise.
1535 """
1536 for c in controllerClasses:
1537 self.waitForDevices_(c)
1540 def device_create(self, dev_config):
1541 """Create a new device.
1543 @param dev_config: device configuration
1544 """
1545 dev_type = sxp.name(dev_config)
1546 devid = self.createDevice(dev_type, dev_config)
1547 self.waitForDevice(dev_type, devid)
1548 self.info['device'].append((dev_type, dev_config))
1549 return self.getDeviceController(dev_type).sxpr(devid)
1552 def device_configure(self, dev_config, devid):
1553 """Configure an existing device.
1554 @param dev_config: device configuration
1555 @param devid: device id
1556 """
1557 deviceClass = sxp.name(dev_config)
1558 self.reconfigureDevice(deviceClass, devid, dev_config)
1561 def pause(self):
1562 xc.domain_pause(self.domid)
1565 def unpause(self):
1566 xc.domain_unpause(self.domid)
1569 ## private:
1571 def restart(self, rename = False):
1572 """Restart the domain after it has exited.
1574 @param rename True if the old domain is to be renamed and preserved,
1575 False if it is to be destroyed.
1576 """
1578 self.configure_bootloader()
1579 config = self.sxpr()
1581 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1582 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1583 self.info['cpus'])])
1585 if self.readVm(RESTART_IN_PROGRESS):
1586 log.error('Xend failed during restart of domain %d. '
1587 'Refusing to restart to avoid loops.',
1588 self.domid)
1589 self.destroy()
1590 return
1592 self.writeVm(RESTART_IN_PROGRESS, 'True')
1594 now = time.time()
1595 rst = self.readVm('xend/previous_restart_time')
1596 if rst:
1597 rst = float(rst)
1598 timeout = now - rst
1599 if timeout < MINIMUM_RESTART_TIME:
1600 log.error(
1601 'VM %s restarting too fast (%f seconds since the last '
1602 'restart). Refusing to restart to avoid loops.',
1603 self.info['name'], timeout)
1604 self.destroy()
1605 return
1607 self.writeVm('xend/previous_restart_time', str(now))
1609 try:
1610 if rename:
1611 self.preserveForRestart()
1612 else:
1613 self.unwatchVm()
1614 self.destroyDomain()
1616 # new_dom's VM will be the same as this domain's VM, except where
1617 # the rename flag has instructed us to call preserveForRestart.
1618 # In that case, it is important that we remove the
1619 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1620 # once the new one is available.
1622 new_dom = None
1623 try:
1624 new_dom = XendDomain.instance().domain_create(config)
1625 new_dom.unpause()
1626 new_dom.removeVm(RESTART_IN_PROGRESS)
1627 except:
1628 if new_dom:
1629 new_dom.removeVm(RESTART_IN_PROGRESS)
1630 new_dom.destroy()
1631 else:
1632 self.removeVm(RESTART_IN_PROGRESS)
1633 raise
1634 except:
1635 log.exception('Failed to restart domain %d.', self.domid)
1638 def preserveForRestart(self):
1639 """Preserve a domain that has been shut down, by giving it a new UUID,
1640 cloning the VM details, and giving it a new name. This allows us to
1641 keep this domain for debugging, but restart a new one in its place
1642 preserving the restart semantics (name and UUID preserved).
1643 """
1645 new_name = self.generateUniqueName()
1646 new_uuid = uuid.toString(uuid.create())
1647 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1648 self.info['name'], self.domid, self.info['uuid'],
1649 new_name, new_uuid)
1650 self.unwatchVm()
1651 self.release_devices()
1652 self.info['name'] = new_name
1653 self.info['uuid'] = new_uuid
1654 self.vmpath = XendDomain.VMROOT + new_uuid
1655 self.storeVmDetails()
1656 self.preserve()
1659 def preserve(self):
1660 log.info("Preserving dead domain %s (%d).", self.info['name'],
1661 self.domid)
1662 self.unwatchVm()
1663 self.storeDom('xend/shutdown_completed', 'True')
1664 self.state_set(STATE_DOM_SHUTDOWN)
1667 # private:
1669 def generateUniqueName(self):
1670 n = 1
1671 while True:
1672 name = "%s-%d" % (self.info['name'], n)
1673 try:
1674 self.check_name(name)
1675 return name
1676 except VmError:
1677 n += 1
1680 def configure_bootloader(self):
1681 """Run the bootloader if we're configured to do so."""
1682 if not self.info['bootloader']:
1683 return
1684 blcfg = None
1685 # FIXME: this assumes that we want to use the first disk device
1686 for (n,c) in self.info['device']:
1687 if not n or not c or n != "vbd":
1688 continue
1689 disk = sxp.child_value(c, "uname")
1690 if disk is None:
1691 continue
1692 fn = blkdev_uname_to_file(disk)
1693 blcfg = bootloader(self.info['bootloader'], fn, 1,
1694 self.info['bootloader_args'],
1695 self.info['image'])
1696 break
1697 if blcfg is None:
1698 msg = "Had a bootloader specified, but can't find disk"
1699 log.error(msg)
1700 raise VmError(msg)
1701 self.info['image'] = blcfg
1704 def send_sysrq(self, key):
1705 asserts.isCharConvertible(key)
1707 self.storeDom("control/sysrq", '%c' % key)
1710 def infoIsSet(self, name):
1711 return name in self.info and self.info[name] is not None
1714 #============================================================================
1715 # Register device controllers and their device config types.
1717 """A map from device-class names to the subclass of DevController that
1718 implements the device control specific to that device-class."""
1719 controllerClasses = {}
1721 def addControllerClass(device_class, cls):
1722 """Register a subclass of DevController to handle the named device-class.
1723 """
1724 cls.deviceClass = device_class
1725 controllerClasses[device_class] = cls
1728 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1729 addControllerClass('vbd', blkif.BlkifController)
1730 addControllerClass('vif', netif.NetifController)
1731 addControllerClass('vtpm', tpmif.TPMifController)
1732 addControllerClass('pci', pciif.PciController)
1733 addControllerClass('ioports', iopif.IOPortsController)
1734 addControllerClass('irq', irqif.IRQController)
1735 addControllerClass('usb', usbif.UsbifController)