ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 10710:800261a88275

[XEND] Prevent uuid double use.

A check_uuid() in this patch checks on uuid of the
VM configuration definition. If specified uuid is
already used with the others VM, the xm create command
does not create the VM. The xm create command error occurs.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Mon Jul 10 15:10:00 2006 +0100 (2006-07-10)
parents 12aaf4a2486b
children 3c74df4f33f0
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 m = self.image.getDomainMemory(self.info['memory'] * 1024)
1306 balloon.free(m)
1307 xc.domain_setmaxmem(self.domid, m)
1309 init_reservation = self.info['memory'] * 1024
1310 if os.uname()[4] == 'ia64':
1311 # Workaround until ia64 properly supports ballooning.
1312 init_reservation = m
1314 xc.domain_memory_increase_reservation(self.domid, init_reservation,
1315 0, 0)
1317 self.createChannels()
1319 channel_details = self.image.createImage()
1321 self.store_mfn = channel_details['store_mfn']
1322 if 'console_mfn' in channel_details:
1323 self.console_mfn = channel_details['console_mfn']
1325 self.introduceDomain()
1327 self.createDevices()
1329 if self.info['bootloader']:
1330 self.image.cleanupBootloading()
1332 self.info['start_time'] = time.time()
1334 except RuntimeError, exn:
1335 raise VmError(str(exn))
1338 ## public:
1340 def cleanupDomain(self):
1341 """Cleanup domain resources; release devices. Idempotent. Nothrow
1342 guarantee."""
1344 self.refresh_shutdown_lock.acquire()
1345 try:
1346 self.unwatchShutdown()
1348 self.release_devices()
1350 if self.image:
1351 try:
1352 self.image.destroy()
1353 except:
1354 log.exception(
1355 "XendDomainInfo.cleanup: image.destroy() failed.")
1356 self.image = None
1358 try:
1359 self.removeDom()
1360 except:
1361 log.exception("Removing domain path failed.")
1363 try:
1364 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1365 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1366 except:
1367 log.exception("Renaming Zombie failed.")
1369 self.state_set(STATE_DOM_SHUTDOWN)
1370 finally:
1371 self.refresh_shutdown_lock.release()
1374 def cleanupVm(self):
1375 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1377 self.unwatchVm()
1379 try:
1380 self.removeVm()
1381 except:
1382 log.exception("Removing VM path failed.")
1385 ## private:
1387 def unwatchVm(self):
1388 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1389 guarantee."""
1391 try:
1392 try:
1393 if self.vmWatch:
1394 self.vmWatch.unwatch()
1395 finally:
1396 self.vmWatch = None
1397 except:
1398 log.exception("Unwatching VM path failed.")
1401 def unwatchShutdown(self):
1402 """Remove the watch on the domain's control/shutdown node, if any.
1403 Idempotent. Nothrow guarantee. Expects to be protected by the
1404 refresh_shutdown_lock."""
1406 try:
1407 try:
1408 if self.shutdownWatch:
1409 self.shutdownWatch.unwatch()
1410 finally:
1411 self.shutdownWatch = None
1412 except:
1413 log.exception("Unwatching control/shutdown failed.")
1416 ## public:
1418 def destroy(self):
1419 """Cleanup VM and destroy domain. Nothrow guarantee."""
1421 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1423 self.cleanupVm()
1424 if self.dompath is not None:
1425 self.destroyDomain()
1428 def destroyDomain(self):
1429 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1431 try:
1432 if self.domid is not None:
1433 xc.domain_destroy(self.domid)
1434 except:
1435 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1437 self.cleanupDomain()
1440 ## private:
1442 def release_devices(self):
1443 """Release all domain's devices. Nothrow guarantee."""
1445 while True:
1446 t = xstransact("%s/device" % self.dompath)
1447 for n in controllerClasses.keys():
1448 for d in t.list(n):
1449 try:
1450 t.remove(d)
1451 except:
1452 # Log and swallow any exceptions in removal --
1453 # there's nothing more we can do.
1454 log.exception(
1455 "Device release failed: %s; %s; %s",
1456 self.info['name'], n, d)
1457 if t.commit():
1458 break
1461 def createChannels(self):
1462 """Create the channels to the domain.
1463 """
1464 self.store_port = self.createChannel()
1465 self.console_port = self.createChannel()
1468 def createChannel(self):
1469 """Create an event channel to the domain.
1470 """
1471 try:
1472 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1473 except:
1474 log.exception("Exception in alloc_unbound(%d)", self.domid)
1475 raise
1478 ## public:
1480 def createDevices(self):
1481 """Create the devices for a vm.
1483 @raise: VmError for invalid devices
1484 """
1486 for (n, c) in self.info['device']:
1487 self.createDevice(n, c)
1489 if self.image:
1490 self.image.createDeviceModel()
1492 ## public:
1494 def testMigrateDevices(self, network, dst):
1495 """ Notify all device about intention of migration
1496 @raise: XendError for a device that cannot be migrated
1497 """
1498 for (n, c) in self.info['device']:
1499 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1500 if rc != 0:
1501 raise XendError("Device of type '%s' refuses migration." % n)
1503 def migrateDevices(self, network, dst, step, domName=''):
1504 """Notify the devices about migration
1505 """
1506 ctr = 0
1507 try:
1508 for (n, c) in self.info['device']:
1509 self.migrateDevice(n, c, network, dst, step, domName)
1510 ctr = ctr + 1
1511 except:
1512 for (n, c) in self.info['device']:
1513 if ctr == 0:
1514 step = step - 1
1515 ctr = ctr - 1
1516 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1517 raise
1519 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1520 step, domName=''):
1521 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1522 network, dst, step, domName)
1524 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1525 dst, step, domName=''):
1526 return self.getDeviceController(deviceClass).recover_migrate(
1527 deviceConfig, network, dst, step, domName)
1529 def waitForDevices(self):
1530 """Wait for this domain's configured devices to connect.
1532 @raise: VmError if any device fails to initialise.
1533 """
1534 for c in controllerClasses:
1535 self.waitForDevices_(c)
1538 def device_create(self, dev_config):
1539 """Create a new device.
1541 @param dev_config: device configuration
1542 """
1543 dev_type = sxp.name(dev_config)
1544 devid = self.createDevice(dev_type, dev_config)
1545 self.waitForDevice(dev_type, devid)
1546 self.info['device'].append((dev_type, dev_config))
1547 return self.getDeviceController(dev_type).sxpr(devid)
1550 def device_configure(self, dev_config, devid):
1551 """Configure an existing device.
1552 @param dev_config: device configuration
1553 @param devid: device id
1554 """
1555 deviceClass = sxp.name(dev_config)
1556 self.reconfigureDevice(deviceClass, devid, dev_config)
1559 def pause(self):
1560 xc.domain_pause(self.domid)
1563 def unpause(self):
1564 xc.domain_unpause(self.domid)
1567 ## private:
1569 def restart(self, rename = False):
1570 """Restart the domain after it has exited.
1572 @param rename True if the old domain is to be renamed and preserved,
1573 False if it is to be destroyed.
1574 """
1576 self.configure_bootloader()
1577 config = self.sxpr()
1579 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1580 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1581 self.info['cpus'])])
1583 if self.readVm(RESTART_IN_PROGRESS):
1584 log.error('Xend failed during restart of domain %d. '
1585 'Refusing to restart to avoid loops.',
1586 self.domid)
1587 self.destroy()
1588 return
1590 self.writeVm(RESTART_IN_PROGRESS, 'True')
1592 now = time.time()
1593 rst = self.readVm('xend/previous_restart_time')
1594 if rst:
1595 rst = float(rst)
1596 timeout = now - rst
1597 if timeout < MINIMUM_RESTART_TIME:
1598 log.error(
1599 'VM %s restarting too fast (%f seconds since the last '
1600 'restart). Refusing to restart to avoid loops.',
1601 self.info['name'], timeout)
1602 self.destroy()
1603 return
1605 self.writeVm('xend/previous_restart_time', str(now))
1607 try:
1608 if rename:
1609 self.preserveForRestart()
1610 else:
1611 self.unwatchVm()
1612 self.destroyDomain()
1614 # new_dom's VM will be the same as this domain's VM, except where
1615 # the rename flag has instructed us to call preserveForRestart.
1616 # In that case, it is important that we remove the
1617 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1618 # once the new one is available.
1620 new_dom = None
1621 try:
1622 new_dom = XendDomain.instance().domain_create(config)
1623 new_dom.unpause()
1624 new_dom.removeVm(RESTART_IN_PROGRESS)
1625 except:
1626 if new_dom:
1627 new_dom.removeVm(RESTART_IN_PROGRESS)
1628 new_dom.destroy()
1629 else:
1630 self.removeVm(RESTART_IN_PROGRESS)
1631 raise
1632 except:
1633 log.exception('Failed to restart domain %d.', self.domid)
1636 def preserveForRestart(self):
1637 """Preserve a domain that has been shut down, by giving it a new UUID,
1638 cloning the VM details, and giving it a new name. This allows us to
1639 keep this domain for debugging, but restart a new one in its place
1640 preserving the restart semantics (name and UUID preserved).
1641 """
1643 new_name = self.generateUniqueName()
1644 new_uuid = uuid.toString(uuid.create())
1645 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1646 self.info['name'], self.domid, self.info['uuid'],
1647 new_name, new_uuid)
1648 self.unwatchVm()
1649 self.release_devices()
1650 self.info['name'] = new_name
1651 self.info['uuid'] = new_uuid
1652 self.vmpath = XendDomain.VMROOT + new_uuid
1653 self.storeVmDetails()
1654 self.preserve()
1657 def preserve(self):
1658 log.info("Preserving dead domain %s (%d).", self.info['name'],
1659 self.domid)
1660 self.unwatchVm()
1661 self.storeDom('xend/shutdown_completed', 'True')
1662 self.state_set(STATE_DOM_SHUTDOWN)
1665 # private:
1667 def generateUniqueName(self):
1668 n = 1
1669 while True:
1670 name = "%s-%d" % (self.info['name'], n)
1671 try:
1672 self.check_name(name)
1673 return name
1674 except VmError:
1675 n += 1
1678 def configure_bootloader(self):
1679 """Run the bootloader if we're configured to do so."""
1680 if not self.info['bootloader']:
1681 return
1682 blcfg = None
1683 # FIXME: this assumes that we want to use the first disk device
1684 for (n,c) in self.info['device']:
1685 if not n or not c or n != "vbd":
1686 continue
1687 disk = sxp.child_value(c, "uname")
1688 if disk is None:
1689 continue
1690 fn = blkdev_uname_to_file(disk)
1691 blcfg = bootloader(self.info['bootloader'], fn, 1,
1692 self.info['bootloader_args'],
1693 self.info['image'])
1694 break
1695 if blcfg is None:
1696 msg = "Had a bootloader specified, but can't find disk"
1697 log.error(msg)
1698 raise VmError(msg)
1699 self.info['image'] = blcfg
1702 def send_sysrq(self, key):
1703 asserts.isCharConvertible(key)
1705 self.storeDom("control/sysrq", '%c' % key)
1708 def infoIsSet(self, name):
1709 return name in self.info and self.info[name] is not None
1712 #============================================================================
1713 # Register device controllers and their device config types.
1715 """A map from device-class names to the subclass of DevController that
1716 implements the device control specific to that device-class."""
1717 controllerClasses = {}
1719 def addControllerClass(device_class, cls):
1720 """Register a subclass of DevController to handle the named device-class.
1721 """
1722 cls.deviceClass = device_class
1723 controllerClasses[device_class] = cls
1726 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1727 addControllerClass('vbd', blkif.BlkifController)
1728 addControllerClass('vif', netif.NetifController)
1729 addControllerClass('vtpm', tpmif.TPMifController)
1730 addControllerClass('pci', pciif.PciController)
1731 addControllerClass('ioports', iopif.IOPortsController)
1732 addControllerClass('irq', irqif.IRQController)
1733 addControllerClass('usb', usbif.UsbifController)