ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 11440:780409e8f0ba

[BLKTAP] Fix in xend to properly destroy blktap devices when deviceClass=='tap'
Submitted by Andres Lagar Cavilla
author jchesterfield@dhcp92.uk.xensource.com
date Wed Sep 06 11:42:29 2006 +0100 (2006-09-06)
parents 65a41e3206ac
children 8013b84df1ac
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 ('shadow_memory', int),
135 ('maxmem', int),
136 ('bootloader', str),
137 ('bootloader_args', str),
138 ('features', str),
139 ('localtime', int),
140 ]
142 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
145 ##
146 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
147 # entries written to the store that cannot be reconfigured on-the-fly.
148 #
149 VM_STORE_ENTRIES = [
150 ('uuid', str),
151 ('vcpus', int),
152 ('vcpu_avail', int),
153 ('memory', int),
154 ('shadow_memory', int),
155 ('maxmem', int),
156 ('start_time', float),
157 ]
159 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
162 #
163 # There are a number of CPU-related fields:
164 #
165 # vcpus: the number of virtual CPUs this domain is configured to use.
166 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
167 # its VCPUs. This is translated to
168 # <dompath>/cpu/<id>/availability = {online,offline} for use
169 # by the guest domain.
170 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
171 # CPUs that that VCPU may use.
172 # cpu: a configuration setting requesting that VCPU 0 is pinned to
173 # the specified physical CPU.
174 #
175 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
176 # across save, restore, migrate, and restart). The other settings are only
177 # specific to the domain, so are lost when the VM moves.
178 #
181 def create(config):
182 """Create a VM from a configuration.
184 @param config configuration
185 @raise: VmError for invalid configuration
186 """
188 log.debug("XendDomainInfo.create(%s)", config)
190 vm = XendDomainInfo(parseConfig(config))
191 try:
192 vm.construct()
193 vm.initDomain()
194 vm.storeVmDetails()
195 vm.storeDomDetails()
196 vm.registerWatches()
197 vm.refreshShutdown()
198 return vm
199 except:
200 log.exception('Domain construction failed')
201 vm.destroy()
202 raise
205 def recreate(xeninfo, priv):
206 """Create the VM object for an existing domain. The domain must not
207 be dying, as the paths in the store should already have been removed,
208 and asking us to recreate them causes problems."""
210 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
212 assert not xeninfo['dying']
214 domid = xeninfo['dom']
215 uuid1 = xeninfo['handle']
216 xeninfo['uuid'] = uuid.toString(uuid1)
217 dompath = GetDomainPath(domid)
218 if not dompath:
219 raise XendError(
220 'No domain path in store for existing domain %d' % domid)
222 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
223 try:
224 vmpath = xstransact.Read(dompath, "vm")
225 if not vmpath:
226 raise XendError(
227 'No vm path in store for existing domain %d' % domid)
228 uuid2_str = xstransact.Read(vmpath, "uuid")
229 if not uuid2_str:
230 raise XendError(
231 'No vm/uuid path in store for existing domain %d' % domid)
233 uuid2 = uuid.fromString(uuid2_str)
235 if uuid1 != uuid2:
236 raise XendError(
237 'Uuid in store does not match uuid for existing domain %d: '
238 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
240 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
242 except Exception, exn:
243 if priv:
244 log.warn(str(exn))
246 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
247 vm.recreateDom()
248 vm.removeVm()
249 vm.storeVmDetails()
250 vm.storeDomDetails()
252 vm.registerWatches()
253 vm.refreshShutdown(xeninfo)
254 return vm
257 def restore(config):
258 """Create a domain and a VM object to do a restore.
260 @param config: domain configuration
261 """
263 log.debug("XendDomainInfo.restore(%s)", config)
265 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
266 try:
267 vm.construct()
268 vm.storeVmDetails()
269 vm.createDevices()
270 vm.createChannels()
271 vm.storeDomDetails()
272 vm.endRestore()
273 return vm
274 except:
275 vm.destroy()
276 raise
279 def parseConfig(config):
280 def get_cfg(name, conv = None):
281 val = sxp.child_value(config, name)
283 if conv and not val is None:
284 try:
285 return conv(val)
286 except TypeError, exn:
287 raise VmError(
288 'Invalid setting %s = %s in configuration: %s' %
289 (name, val, str(exn)))
290 else:
291 return val
294 log.debug("parseConfig: config is %s", config)
296 result = {}
298 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
299 result[e[0]] = get_cfg(e[0], e[1])
301 result['cpu'] = get_cfg('cpu', int)
302 result['cpus'] = get_cfg('cpus', str)
303 result['image'] = get_cfg('image')
304 tmp_security = get_cfg('security')
305 if tmp_security:
306 result['security'] = tmp_security
308 try:
309 if result['image']:
310 v = sxp.child_value(result['image'], 'vcpus')
311 if result['vcpus'] is None and v is not None:
312 result['vcpus'] = int(v)
313 elif v is not None and int(v) != result['vcpus']:
314 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
315 ' Using %s VCPUs for VM %s.') %
316 (result['vcpus'], v, result['uuid']))
317 result['vcpus'] = int(v)
318 except TypeError, exn:
319 raise VmError(
320 'Invalid configuration setting: vcpus = %s: %s' %
321 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
323 try:
324 # support legacy config files with 'cpu' parameter
325 # NB: prepending to list to support previous behavior
326 # where 'cpu' parameter pinned VCPU0.
327 if result['cpu']:
328 if result['cpus']:
329 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
330 else:
331 result['cpus'] = str(result['cpu'])
333 # convert 'cpus' string to list of ints
334 # 'cpus' supports a list of ranges (0-3), seperated by
335 # commas, and negation, (^1).
336 # Precedence is settled by order of the string:
337 # "0-3,^1" -> [0,2,3]
338 # "0-3,^1,1" -> [0,1,2,3]
339 if result['cpus']:
340 cpus = []
341 for c in result['cpus'].split(','):
342 if c.find('-') != -1:
343 (x,y) = c.split('-')
344 for i in range(int(x),int(y)+1):
345 cpus.append(int(i))
346 else:
347 # remove this element from the list
348 if c[0] == '^':
349 cpus = [x for x in cpus if x != int(c[1:])]
350 else:
351 cpus.append(int(c))
353 result['cpus'] = cpus
355 except ValueError, exn:
356 raise VmError(
357 'Invalid configuration setting: cpus = %s: %s' %
358 (result['cpus'], exn))
360 result['backend'] = []
361 for c in sxp.children(config, 'backend'):
362 result['backend'].append(sxp.name(sxp.child0(c)))
364 result['device'] = []
365 for d in sxp.children(config, 'device'):
366 c = sxp.child0(d)
367 result['device'].append((sxp.name(c), c))
369 # Configuration option "restart" is deprecated. Parse it, but
370 # let on_xyz override it if they are present.
371 restart = get_cfg('restart')
372 if restart:
373 def handle_restart(event, val):
374 if result[event] is None:
375 result[event] = val
377 if restart == "onreboot":
378 handle_restart('on_poweroff', 'destroy')
379 handle_restart('on_reboot', 'restart')
380 handle_restart('on_crash', 'destroy')
381 elif restart == "always":
382 handle_restart('on_poweroff', 'restart')
383 handle_restart('on_reboot', 'restart')
384 handle_restart('on_crash', 'restart')
385 elif restart == "never":
386 handle_restart('on_poweroff', 'destroy')
387 handle_restart('on_reboot', 'destroy')
388 handle_restart('on_crash', 'destroy')
389 else:
390 log.warn("Ignoring malformed and deprecated config option "
391 "restart = %s", restart)
393 log.debug("parseConfig: result is %s", result)
394 return result
397 def domain_by_name(name):
398 return XendDomain.instance().domain_lookup_by_name_nr(name)
401 def shutdown_reason(code):
402 """Get a shutdown reason from a code.
404 @param code: shutdown code
405 @type code: int
406 @return: shutdown reason
407 @rtype: string
408 """
409 return shutdown_reasons.get(code, "?")
411 def dom_get(dom):
412 """Get info from xen for an existing domain.
414 @param dom: domain id
415 @return: info or None
416 """
417 try:
418 domlist = xc.domain_getinfo(dom, 1)
419 if domlist and dom == domlist[0]['dom']:
420 return domlist[0]
421 except Exception, err:
422 # ignore missing domain
423 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
424 return None
427 class XendDomainInfo:
429 def __init__(self, info, domid = None, dompath = None, augment = False,
430 priv = False, resume = False):
432 self.info = info
434 if not self.infoIsSet('uuid'):
435 self.info['uuid'] = uuid.toString(uuid.create())
437 if domid is not None:
438 self.domid = domid
439 elif 'dom' in info:
440 self.domid = int(info['dom'])
441 else:
442 self.domid = None
444 self.vmpath = XendDomain.VMROOT + self.info['uuid']
445 self.dompath = dompath
447 if augment:
448 self.augmentInfo(priv)
450 self.validateInfo()
452 self.image = None
453 self.security = None
454 self.store_port = None
455 self.store_mfn = None
456 self.console_port = None
457 self.console_mfn = None
459 self.vmWatch = None
460 self.shutdownWatch = None
462 self.shutdownStartTime = None
464 self.state = STATE_DOM_OK
465 self.state_updated = threading.Condition()
466 self.refresh_shutdown_lock = threading.Condition()
468 self.setResume(resume)
470 ## private:
472 def readVMDetails(self, params):
473 """Read the specified parameters from the store.
474 """
475 try:
476 return self.gatherVm(*params)
477 except ValueError:
478 # One of the int/float entries in params has a corresponding store
479 # entry that is invalid. We recover, because older versions of
480 # Xend may have put the entry there (memory/target, for example),
481 # but this is in general a bad situation to have reached.
482 log.exception(
483 "Store corrupted at %s! Domain %d's configuration may be "
484 "affected.", self.vmpath, self.domid)
485 return []
488 def storeChanged(self, _):
489 log.trace("XendDomainInfo.storeChanged");
491 changed = False
493 def f(x, y):
494 if y is not None and self.info[x[0]] != y:
495 self.info[x[0]] = y
496 changed = True
498 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
500 im = self.readVm('image')
501 current_im = self.info['image']
502 if (im is not None and
503 (current_im is None or sxp.to_string(current_im) != im)):
504 self.info['image'] = sxp.from_string(im)
505 changed = True
507 if changed:
508 # Update the domain section of the store, as this contains some
509 # parameters derived from the VM configuration.
510 self.storeDomDetails()
512 return 1
515 def augmentInfo(self, priv):
516 """Augment self.info, as given to us through {@link #recreate}, with
517 values taken from the store. This recovers those values known to xend
518 but not to the hypervisor.
519 """
520 def useIfNeeded(name, val):
521 if not self.infoIsSet(name) and val is not None:
522 self.info[name] = val
524 if priv:
525 entries = VM_STORE_ENTRIES[:]
526 entries.remove(('memory', int))
527 entries.remove(('maxmem', int))
528 else:
529 entries = VM_STORE_ENTRIES
530 entries.append(('image', str))
531 entries.append(('security', str))
533 map(lambda x, y: useIfNeeded(x[0], y), entries,
534 self.readVMDetails(entries))
536 device = []
537 for c in controllerClasses:
538 devconfig = self.getDeviceConfigurations(c)
539 if devconfig:
540 device.extend(map(lambda x: (c, x), devconfig))
541 useIfNeeded('device', device)
544 def validateInfo(self):
545 """Validate and normalise the info block. This has either been parsed
546 by parseConfig, or received from xc through recreate and augmented by
547 the current store contents.
548 """
549 def defaultInfo(name, val):
550 if not self.infoIsSet(name):
551 self.info[name] = val()
553 try:
554 defaultInfo('name', lambda: "Domain-%d" % self.domid)
555 defaultInfo('on_poweroff', lambda: "destroy")
556 defaultInfo('on_reboot', lambda: "restart")
557 defaultInfo('on_crash', lambda: "restart")
558 defaultInfo('features', lambda: "")
559 defaultInfo('cpu', lambda: None)
560 defaultInfo('cpus', lambda: [])
561 defaultInfo('cpu_weight', lambda: 1.0)
563 # some domains don't have a config file (e.g. dom0 )
564 # to set number of vcpus so we derive available cpus
565 # from max_vcpu_id which is present for running domains.
566 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
567 avail = int(self.info['max_vcpu_id'])+1
568 else:
569 avail = int(1)
571 defaultInfo('vcpus', lambda: avail)
572 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
573 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
574 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
576 defaultInfo('memory', lambda: 0)
577 defaultInfo('shadow_memory', lambda: 0)
578 defaultInfo('maxmem', lambda: 0)
579 defaultInfo('bootloader', lambda: None)
580 defaultInfo('bootloader_args', lambda: None)
581 defaultInfo('backend', lambda: [])
582 defaultInfo('device', lambda: [])
583 defaultInfo('image', lambda: None)
584 defaultInfo('security', lambda: None)
586 self.check_name(self.info['name'])
588 if isinstance(self.info['image'], str):
589 self.info['image'] = sxp.from_string(self.info['image'])
591 if isinstance(self.info['security'], str):
592 self.info['security'] = sxp.from_string(self.info['security'])
594 if self.info['memory'] == 0:
595 if self.infoIsSet('mem_kb'):
596 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
598 if self.info['maxmem'] < self.info['memory']:
599 self.info['maxmem'] = self.info['memory']
601 for (n, c) in self.info['device']:
602 if not n or not c or n not in controllerClasses:
603 raise VmError('invalid device (%s, %s)' %
604 (str(n), str(c)))
606 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
607 if self.info[event] not in restart_modes:
608 raise VmError('invalid restart event: %s = %s' %
609 (event, str(self.info[event])))
611 except KeyError, exn:
612 log.exception(exn)
613 raise VmError('Unspecified domain detail: %s' % exn)
616 def readVm(self, *args):
617 return xstransact.Read(self.vmpath, *args)
619 def writeVm(self, *args):
620 return xstransact.Write(self.vmpath, *args)
622 def removeVm(self, *args):
623 return xstransact.Remove(self.vmpath, *args)
625 def gatherVm(self, *args):
626 return xstransact.Gather(self.vmpath, *args)
629 ## public:
631 def storeVm(self, *args):
632 return xstransact.Store(self.vmpath, *args)
635 ## private:
637 def readDom(self, *args):
638 return xstransact.Read(self.dompath, *args)
640 def writeDom(self, *args):
641 return xstransact.Write(self.dompath, *args)
644 ## public:
646 def removeDom(self, *args):
647 return xstransact.Remove(self.dompath, *args)
649 def recreateDom(self):
650 complete(self.dompath, lambda t: self._recreateDom(t))
652 def _recreateDom(self, t):
653 t.remove()
654 t.mkdir()
655 t.set_permissions({ 'dom' : self.domid })
658 ## private:
660 def storeDom(self, *args):
661 return xstransact.Store(self.dompath, *args)
664 ## public:
666 def completeRestore(self, store_mfn, console_mfn):
668 log.debug("XendDomainInfo.completeRestore")
670 self.store_mfn = store_mfn
671 self.console_mfn = console_mfn
673 self.introduceDomain()
674 self.storeDomDetails()
675 self.registerWatches()
676 self.refreshShutdown()
678 log.debug("XendDomainInfo.completeRestore done")
681 def storeVmDetails(self):
682 to_store = {}
684 for k in VM_STORE_ENTRIES:
685 if self.infoIsSet(k[0]):
686 to_store[k[0]] = str(self.info[k[0]])
688 if self.infoIsSet('image'):
689 to_store['image'] = sxp.to_string(self.info['image'])
691 if self.infoIsSet('security'):
692 security = self.info['security']
693 to_store['security'] = sxp.to_string(security)
694 for idx in range(0, len(security)):
695 if security[idx][0] == 'access_control':
696 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
697 for aidx in range(1, len(security[idx])):
698 if security[idx][aidx][0] == 'label':
699 to_store['security/access_control/label'] = security[idx][aidx][1]
700 if security[idx][aidx][0] == 'policy':
701 to_store['security/access_control/policy'] = security[idx][aidx][1]
702 if security[idx][0] == 'ssidref':
703 to_store['security/ssidref'] = str(security[idx][1])
705 if not self.readVm('xend/restart_count'):
706 to_store['xend/restart_count'] = str(0)
708 log.debug("Storing VM details: %s", to_store)
710 self.writeVm(to_store)
711 self.setVmPermissions()
714 def setVmPermissions(self):
715 """Allow the guest domain to read its UUID. We don't allow it to
716 access any other entry, for security."""
717 xstransact.SetPermissions('%s/uuid' % self.vmpath,
718 { 'dom' : self.domid,
719 'read' : True,
720 'write' : False })
723 def storeDomDetails(self):
724 to_store = {
725 'domid': str(self.domid),
726 'vm': self.vmpath,
727 'name': self.info['name'],
728 'console/limit': str(xroot.get_console_limit() * 1024),
729 'memory/target': str(self.info['memory'] * 1024)
730 }
732 def f(n, v):
733 if v is not None:
734 to_store[n] = str(v)
736 f('console/port', self.console_port)
737 f('console/ring-ref', self.console_mfn)
738 f('store/port', self.store_port)
739 f('store/ring-ref', self.store_mfn)
741 to_store.update(self.vcpuDomDetails())
743 log.debug("Storing domain details: %s", to_store)
745 self.writeDom(to_store)
748 ## private:
750 def vcpuDomDetails(self):
751 def availability(n):
752 if self.info['vcpu_avail'] & (1 << n):
753 return 'online'
754 else:
755 return 'offline'
757 result = {}
758 for v in range(0, self.info['vcpus']):
759 result["cpu/%d/availability" % v] = availability(v)
760 return result
763 ## public:
765 def registerWatches(self):
766 """Register a watch on this VM's entries in the store, and the
767 domain's control/shutdown node, so that when they are changed
768 externally, we keep up to date. This should only be called by {@link
769 #create}, {@link #recreate}, or {@link #restore}, once the domain's
770 details have been written, but before the new instance is returned."""
771 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
772 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
773 self.handleShutdownWatch)
776 def getDomid(self):
777 return self.domid
779 def setName(self, name):
780 self.check_name(name)
781 self.info['name'] = name
782 self.storeVm("name", name)
784 def getName(self):
785 return self.info['name']
787 def getDomainPath(self):
788 return self.dompath
791 def getStorePort(self):
792 """For use only by image.py and XendCheckpoint.py."""
793 return self.store_port
796 def getConsolePort(self):
797 """For use only by image.py and XendCheckpoint.py"""
798 return self.console_port
800 def getFeatures(self):
801 """For use only by image.py."""
802 return self.info['features']
804 def getVCpuCount(self):
805 return self.info['vcpus']
808 def setVCpuCount(self, vcpus):
809 self.info['vcpu_avail'] = (1 << vcpus) - 1
810 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
811 self.writeDom(self.vcpuDomDetails())
813 def getLabel(self):
814 return security.get_security_info(self.info, 'label')
816 def getMemoryTarget(self):
817 """Get this domain's target memory size, in KB."""
818 return self.info['memory'] * 1024
820 def getResume(self):
821 return "%s" % self.info['resume']
823 def endRestore(self):
824 self.setResume(False)
826 def setResume(self, state):
827 self.info['resume'] = state
829 def getRestartCount(self):
830 return self.readVm('xend/restart_count')
832 def refreshShutdown(self, xeninfo = None):
833 # If set at the end of this method, a restart is required, with the
834 # given reason. This restart has to be done out of the scope of
835 # refresh_shutdown_lock.
836 restart_reason = None
838 self.refresh_shutdown_lock.acquire()
839 try:
840 if xeninfo is None:
841 xeninfo = dom_get(self.domid)
842 if xeninfo is None:
843 # The domain no longer exists. This will occur if we have
844 # scheduled a timer to check for shutdown timeouts and the
845 # shutdown succeeded. It will also occur if someone
846 # destroys a domain beneath us. We clean up the domain,
847 # just in case, but we can't clean up the VM, because that
848 # VM may have migrated to a different domain on this
849 # machine.
850 self.cleanupDomain()
851 return
853 if xeninfo['dying']:
854 # Dying means that a domain has been destroyed, but has not
855 # yet been cleaned up by Xen. This state could persist
856 # indefinitely if, for example, another domain has some of its
857 # pages mapped. We might like to diagnose this problem in the
858 # future, but for now all we do is make sure that it's not us
859 # holding the pages, by calling cleanupDomain. We can't
860 # clean up the VM, as above.
861 self.cleanupDomain()
862 return
864 elif xeninfo['crashed']:
865 if self.readDom('xend/shutdown_completed'):
866 # We've seen this shutdown already, but we are preserving
867 # the domain for debugging. Leave it alone.
868 return
870 log.warn('Domain has crashed: name=%s id=%d.',
871 self.info['name'], self.domid)
873 if xroot.get_enable_dump():
874 self.dumpCore()
876 restart_reason = 'crash'
878 elif xeninfo['shutdown']:
879 if self.readDom('xend/shutdown_completed'):
880 # We've seen this shutdown already, but we are preserving
881 # the domain for debugging. Leave it alone.
882 return
884 else:
885 reason = shutdown_reason(xeninfo['shutdown_reason'])
887 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
888 self.info['name'], self.domid, reason)
890 self.clearRestart()
892 if reason == 'suspend':
893 self.state_set(STATE_DOM_SHUTDOWN)
894 # Don't destroy the domain. XendCheckpoint will do
895 # this once it has finished. However, stop watching
896 # the VM path now, otherwise we will end up with one
897 # watch for the old domain, and one for the new.
898 self.unwatchVm()
899 elif reason in ['poweroff', 'reboot']:
900 restart_reason = reason
901 else:
902 self.destroy()
904 elif self.dompath is None:
905 # We have yet to manage to call introduceDomain on this
906 # domain. This can happen if a restore is in progress, or has
907 # failed. Ignore this domain.
908 pass
909 else:
910 # Domain is alive. If we are shutting it down, then check
911 # the timeout on that, and destroy it if necessary.
913 if self.shutdownStartTime:
914 timeout = (SHUTDOWN_TIMEOUT - time.time() +
915 self.shutdownStartTime)
916 if timeout < 0:
917 log.info(
918 "Domain shutdown timeout expired: name=%s id=%s",
919 self.info['name'], self.domid)
920 self.destroy()
921 finally:
922 self.refresh_shutdown_lock.release()
924 if restart_reason:
925 self.maybeRestart(restart_reason)
928 def handleShutdownWatch(self, _):
929 log.debug('XendDomainInfo.handleShutdownWatch')
931 reason = self.readDom('control/shutdown')
933 if reason and reason != 'suspend':
934 sst = self.readDom('xend/shutdown_start_time')
935 now = time.time()
936 if sst:
937 self.shutdownStartTime = float(sst)
938 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
939 else:
940 self.shutdownStartTime = now
941 self.storeDom('xend/shutdown_start_time', now)
942 timeout = SHUTDOWN_TIMEOUT
944 log.trace(
945 "Scheduling refreshShutdown on domain %d in %ds.",
946 self.domid, timeout)
947 threading.Timer(timeout, self.refreshShutdown).start()
949 return True
952 def shutdown(self, reason):
953 if not reason in shutdown_reasons.values():
954 raise XendError('Invalid reason: %s' % reason)
955 if self.domid == 0:
956 raise XendError("Can't specify Domain-0")
957 self.storeDom("control/shutdown", reason)
960 ## private:
962 def clearRestart(self):
963 self.removeDom("xend/shutdown_start_time")
966 def maybeRestart(self, reason):
967 # Dispatch to the correct method based upon the configured on_{reason}
968 # behaviour.
969 {"destroy" : self.destroy,
970 "restart" : self.restart,
971 "preserve" : self.preserve,
972 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
975 def renameRestart(self):
976 self.restart(True)
979 def dumpCore(self):
980 """Create a core dump for this domain. Nothrow guarantee."""
982 try:
983 corefile = "/var/xen/dump/%s.%s.core" % (self.info['name'],
984 self.domid)
985 xc.domain_dumpcore(self.domid, corefile)
987 except:
988 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
989 self.domid, self.info['name'])
992 ## public:
994 def setMemoryTarget(self, target):
995 """Set the memory target of this domain.
996 @param target In MiB.
997 """
998 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
999 self.info['name'], self.domid, target)
1001 self.info['memory'] = target
1002 self.storeVm("memory", target)
1003 self.storeDom("memory/target", target << 10)
1006 def update(self, info = None):
1007 """Update with info from xc.domain_getinfo().
1008 """
1010 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1011 if not info:
1012 info = dom_get(self.domid)
1013 if not info:
1014 return
1016 #manually update ssidref / security fields
1017 if security.on() and info.has_key('ssidref'):
1018 if (info['ssidref'] != 0) and self.info.has_key('security'):
1019 security_field = self.info['security']
1020 if not security_field:
1021 #create new security element
1022 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1023 #ssidref field not used any longer
1024 info.pop('ssidref')
1026 self.info.update(info)
1027 self.validateInfo()
1028 self.refreshShutdown(info)
1030 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1031 self.info)
1034 ## private:
1036 def state_set(self, state):
1037 self.state_updated.acquire()
1038 try:
1039 if self.state != state:
1040 self.state = state
1041 self.state_updated.notifyAll()
1042 finally:
1043 self.state_updated.release()
1046 ## public:
1048 def waitForShutdown(self):
1049 self.state_updated.acquire()
1050 try:
1051 while self.state == STATE_DOM_OK:
1052 self.state_updated.wait()
1053 finally:
1054 self.state_updated.release()
1057 def __str__(self):
1058 s = "<domain"
1059 s += " id=" + str(self.domid)
1060 s += " name=" + self.info['name']
1061 s += " memory=" + str(self.info['memory'])
1062 s += ">"
1063 return s
1065 __repr__ = __str__
1068 ## private:
1070 def createDevice(self, deviceClass, devconfig):
1071 return self.getDeviceController(deviceClass).createDevice(devconfig)
1074 def waitForDevices_(self, deviceClass):
1075 return self.getDeviceController(deviceClass).waitForDevices()
1078 def waitForDevice(self, deviceClass, devid):
1079 return self.getDeviceController(deviceClass).waitForDevice(devid)
1082 def reconfigureDevice(self, deviceClass, devid, devconfig):
1083 return self.getDeviceController(deviceClass).reconfigureDevice(
1084 devid, devconfig)
1087 ## public:
1089 def destroyDevice(self, deviceClass, devid):
1090 if type(devid) is str:
1091 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
1092 for entry in xstransact.List(devicePath):
1093 backend = xstransact.Read('%s/%s' % (devicePath, entry), "backend")
1094 devName = xstransact.Read(backend, "dev")
1095 if devName == devid:
1096 # We found the integer matching our devid, use it instead
1097 devid = entry
1098 break
1099 return self.getDeviceController(deviceClass).destroyDevice(devid)
1102 def getDeviceSxprs(self, deviceClass):
1103 return self.getDeviceController(deviceClass).sxprs()
1106 ## private:
1108 def getDeviceConfigurations(self, deviceClass):
1109 return self.getDeviceController(deviceClass).configurations()
1112 def getDeviceController(self, name):
1113 if name not in controllerClasses:
1114 raise XendError("unknown device type: " + str(name))
1116 return controllerClasses[name](self)
1119 ## public:
1121 def sxpr(self):
1122 sxpr = ['domain',
1123 ['domid', self.domid]]
1125 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1126 if self.infoIsSet(e[0]):
1127 sxpr.append([e[0], self.info[e[0]]])
1129 if self.infoIsSet('image'):
1130 sxpr.append(['image', self.info['image']])
1132 if self.infoIsSet('security'):
1133 sxpr.append(['security', self.info['security']])
1135 for cls in controllerClasses:
1136 for config in self.getDeviceConfigurations(cls):
1137 sxpr.append(['device', config])
1139 def stateChar(name):
1140 if name in self.info:
1141 if self.info[name]:
1142 return name[0]
1143 else:
1144 return '-'
1145 else:
1146 return '?'
1148 state = reduce(
1149 lambda x, y: x + y,
1150 map(stateChar,
1151 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1152 'dying']))
1154 sxpr.append(['state', state])
1155 if self.infoIsSet('shutdown'):
1156 reason = shutdown_reason(self.info['shutdown_reason'])
1157 sxpr.append(['shutdown_reason', reason])
1158 if self.infoIsSet('cpu_time'):
1159 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1160 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1162 if self.infoIsSet('start_time'):
1163 up_time = time.time() - self.info['start_time']
1164 sxpr.append(['up_time', str(up_time) ])
1165 sxpr.append(['start_time', str(self.info['start_time']) ])
1167 if self.store_mfn:
1168 sxpr.append(['store_mfn', self.store_mfn])
1169 if self.console_mfn:
1170 sxpr.append(['console_mfn', self.console_mfn])
1172 return sxpr
1175 def getVCPUInfo(self):
1176 try:
1177 # We include the domain name and ID, to help xm.
1178 sxpr = ['domain',
1179 ['domid', self.domid],
1180 ['name', self.info['name']],
1181 ['vcpu_count', self.info['online_vcpus']]]
1183 for i in range(0, self.info['max_vcpu_id']+1):
1184 info = xc.vcpu_getinfo(self.domid, i)
1186 sxpr.append(['vcpu',
1187 ['number', i],
1188 ['online', info['online']],
1189 ['blocked', info['blocked']],
1190 ['running', info['running']],
1191 ['cpu_time', info['cpu_time'] / 1e9],
1192 ['cpu', info['cpu']],
1193 ['cpumap', info['cpumap']]])
1195 return sxpr
1197 except RuntimeError, exn:
1198 raise XendError(str(exn))
1201 ## private:
1203 def check_name(self, name):
1204 """Check if a vm name is valid. Valid names contain alphabetic characters,
1205 digits, or characters in '_-.:/+'.
1206 The same name cannot be used for more than one vm at the same time.
1208 @param name: name
1209 @raise: VmError if invalid
1210 """
1211 if name is None or name == '':
1212 raise VmError('missing vm name')
1213 for c in name:
1214 if c in string.digits: continue
1215 if c in '_-.:/+': continue
1216 if c in string.ascii_letters: continue
1217 raise VmError('invalid vm name')
1219 dominfo = domain_by_name(name)
1220 if not dominfo:
1221 return
1222 if self.domid is None:
1223 raise VmError("VM name '%s' already in use by domain %d" %
1224 (name, dominfo.domid))
1225 if dominfo.domid != self.domid:
1226 raise VmError("VM name '%s' is used in both domains %d and %d" %
1227 (name, self.domid, dominfo.domid))
1230 def construct(self):
1231 """Construct the domain.
1233 @raise: VmError on error
1234 """
1236 log.debug('XendDomainInfo.construct: %s',
1237 self.domid)
1239 self.domid = xc.domain_create(
1240 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1241 handle = uuid.fromString(self.info['uuid']))
1243 if self.domid < 0:
1244 raise VmError('Creating domain failed: name=%s' %
1245 self.info['name'])
1247 self.dompath = GetDomainPath(self.domid)
1249 self.recreateDom()
1251 # Set maximum number of vcpus in domain
1252 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1255 def introduceDomain(self):
1256 assert self.domid is not None
1257 assert self.store_mfn is not None
1258 assert self.store_port is not None
1260 try:
1261 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1262 except RuntimeError, exn:
1263 raise XendError(str(exn))
1266 def initDomain(self):
1267 log.debug('XendDomainInfo.initDomain: %s %s',
1268 self.domid,
1269 self.info['cpu_weight'])
1271 # if we have a boot loader but no image, then we need to set things
1272 # up by running the boot loader non-interactively
1273 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1274 self.configure_bootloader()
1276 if not self.infoIsSet('image'):
1277 raise VmError('Missing image in configuration')
1279 try:
1280 self.image = image.create(self,
1281 self.info['image'],
1282 self.info['device'])
1284 localtime = self.info['localtime']
1285 if localtime is not None and localtime == 1:
1286 xc.domain_set_time_offset(self.domid)
1288 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1290 # repin domain vcpus if a restricted cpus list is provided
1291 # this is done prior to memory allocation to aide in memory
1292 # distribution for NUMA systems.
1293 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1294 for v in range(0, self.info['max_vcpu_id']+1):
1295 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1297 # Use architecture- and image-specific calculations to determine
1298 # the various headrooms necessary, given the raw configured
1299 # values.
1300 # reservation, maxmem, memory, and shadow are all in KiB.
1301 reservation = self.image.getRequiredInitialReservation(
1302 self.info['memory'] * 1024)
1303 maxmem = self.image.getRequiredAvailableMemory(
1304 self.info['maxmem'] * 1024)
1305 memory = self.image.getRequiredAvailableMemory(
1306 self.info['memory'] * 1024)
1307 shadow = self.image.getRequiredShadowMemory(
1308 self.info['shadow_memory'] * 1024,
1309 self.info['maxmem'] * 1024)
1311 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1312 # takes MiB and we must not round down and end up under-providing.
1313 shadow = ((shadow + 1023) / 1024) * 1024
1315 # set memory limit
1316 xc.domain_setmaxmem(self.domid, maxmem)
1318 # Make sure there's enough RAM available for the domain
1319 balloon.free(memory + shadow)
1321 # Set up the shadow memory
1322 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1323 self.info['shadow_memory'] = shadow_cur
1325 # initial memory reservation
1326 xc.domain_memory_increase_reservation(self.domid, reservation, 0,
1327 0)
1329 self.createChannels()
1331 channel_details = self.image.createImage()
1333 self.store_mfn = channel_details['store_mfn']
1334 if 'console_mfn' in channel_details:
1335 self.console_mfn = channel_details['console_mfn']
1337 self.introduceDomain()
1339 self.createDevices()
1341 if self.info['bootloader']:
1342 self.image.cleanupBootloading()
1344 self.info['start_time'] = time.time()
1346 except RuntimeError, exn:
1347 raise VmError(str(exn))
1350 ## public:
1352 def cleanupDomain(self):
1353 """Cleanup domain resources; release devices. Idempotent. Nothrow
1354 guarantee."""
1356 self.refresh_shutdown_lock.acquire()
1357 try:
1358 self.unwatchShutdown()
1360 self.release_devices()
1362 if self.image:
1363 try:
1364 self.image.destroy()
1365 except:
1366 log.exception(
1367 "XendDomainInfo.cleanup: image.destroy() failed.")
1368 self.image = None
1370 try:
1371 self.removeDom()
1372 except:
1373 log.exception("Removing domain path failed.")
1375 try:
1376 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1377 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1378 except:
1379 log.exception("Renaming Zombie failed.")
1381 self.state_set(STATE_DOM_SHUTDOWN)
1382 finally:
1383 self.refresh_shutdown_lock.release()
1386 def cleanupVm(self):
1387 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1389 self.unwatchVm()
1391 try:
1392 self.removeVm()
1393 except:
1394 log.exception("Removing VM path failed.")
1397 ## private:
1399 def unwatchVm(self):
1400 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1401 guarantee."""
1403 try:
1404 try:
1405 if self.vmWatch:
1406 self.vmWatch.unwatch()
1407 finally:
1408 self.vmWatch = None
1409 except:
1410 log.exception("Unwatching VM path failed.")
1413 def unwatchShutdown(self):
1414 """Remove the watch on the domain's control/shutdown node, if any.
1415 Idempotent. Nothrow guarantee. Expects to be protected by the
1416 refresh_shutdown_lock."""
1418 try:
1419 try:
1420 if self.shutdownWatch:
1421 self.shutdownWatch.unwatch()
1422 finally:
1423 self.shutdownWatch = None
1424 except:
1425 log.exception("Unwatching control/shutdown failed.")
1428 ## public:
1430 def destroy(self):
1431 """Cleanup VM and destroy domain. Nothrow guarantee."""
1433 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1435 self.cleanupVm()
1436 if self.dompath is not None:
1437 self.destroyDomain()
1440 def destroyDomain(self):
1441 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1443 try:
1444 if self.domid is not None:
1445 xc.domain_destroy(self.domid)
1446 except:
1447 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1449 self.cleanupDomain()
1452 ## private:
1454 def release_devices(self):
1455 """Release all domain's devices. Nothrow guarantee."""
1457 while True:
1458 t = xstransact("%s/device" % self.dompath)
1459 for n in controllerClasses.keys():
1460 for d in t.list(n):
1461 try:
1462 t.remove(d)
1463 except:
1464 # Log and swallow any exceptions in removal --
1465 # there's nothing more we can do.
1466 log.exception(
1467 "Device release failed: %s; %s; %s",
1468 self.info['name'], n, d)
1469 if t.commit():
1470 break
1473 def createChannels(self):
1474 """Create the channels to the domain.
1475 """
1476 self.store_port = self.createChannel()
1477 self.console_port = self.createChannel()
1480 def createChannel(self):
1481 """Create an event channel to the domain.
1482 """
1483 try:
1484 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1485 except:
1486 log.exception("Exception in alloc_unbound(%d)", self.domid)
1487 raise
1490 ## public:
1492 def createDevices(self):
1493 """Create the devices for a vm.
1495 @raise: VmError for invalid devices
1496 """
1498 for (n, c) in self.info['device']:
1499 self.createDevice(n, c)
1501 if self.image:
1502 self.image.createDeviceModel()
1504 ## public:
1506 def testMigrateDevices(self, network, dst):
1507 """ Notify all device about intention of migration
1508 @raise: XendError for a device that cannot be migrated
1509 """
1510 for (n, c) in self.info['device']:
1511 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1512 if rc != 0:
1513 raise XendError("Device of type '%s' refuses migration." % n)
1515 def testDeviceComplete(self):
1516 """ For Block IO migration safety we must ensure that
1517 the device has shutdown correctly, i.e. all blocks are
1518 flushed to disk
1519 """
1520 while True:
1521 test = 0
1522 for i in self.getDeviceController('vbd').deviceIDs():
1523 test = 1
1524 log.info("Dev %s still active, looping...", i)
1525 time.sleep(0.1)
1527 if test == 0:
1528 break
1530 def migrateDevices(self, network, dst, step, domName=''):
1531 """Notify the devices about migration
1532 """
1533 ctr = 0
1534 try:
1535 for (n, c) in self.info['device']:
1536 self.migrateDevice(n, c, network, dst, step, domName)
1537 ctr = ctr + 1
1538 except:
1539 for (n, c) in self.info['device']:
1540 if ctr == 0:
1541 step = step - 1
1542 ctr = ctr - 1
1543 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1544 raise
1546 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1547 step, domName=''):
1548 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1549 network, dst, step, domName)
1551 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1552 dst, step, domName=''):
1553 return self.getDeviceController(deviceClass).recover_migrate(
1554 deviceConfig, network, dst, step, domName)
1556 def waitForDevices(self):
1557 """Wait for this domain's configured devices to connect.
1559 @raise: VmError if any device fails to initialise.
1560 """
1561 for c in controllerClasses:
1562 self.waitForDevices_(c)
1565 def device_create(self, dev_config):
1566 """Create a new device.
1568 @param dev_config: device configuration
1569 """
1570 dev_type = sxp.name(dev_config)
1571 devid = self.createDevice(dev_type, dev_config)
1572 self.waitForDevice(dev_type, devid)
1573 self.info['device'].append((dev_type, dev_config))
1574 return self.getDeviceController(dev_type).sxpr(devid)
1577 def device_configure(self, dev_config):
1578 """Configure an existing device.
1579 @param dev_config: device configuration
1580 """
1581 deviceClass = sxp.name(dev_config)
1582 self.reconfigureDevice(deviceClass, None, dev_config)
1585 def pause(self):
1586 xc.domain_pause(self.domid)
1589 def unpause(self):
1590 xc.domain_unpause(self.domid)
1593 ## private:
1595 def restart(self, rename = False):
1596 """Restart the domain after it has exited.
1598 @param rename True if the old domain is to be renamed and preserved,
1599 False if it is to be destroyed.
1600 """
1602 self.configure_bootloader()
1603 config = self.sxpr()
1605 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1606 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1607 self.info['cpus'])])
1609 if self.readVm(RESTART_IN_PROGRESS):
1610 log.error('Xend failed during restart of domain %d. '
1611 'Refusing to restart to avoid loops.',
1612 self.domid)
1613 self.destroy()
1614 return
1616 self.writeVm(RESTART_IN_PROGRESS, 'True')
1618 now = time.time()
1619 rst = self.readVm('xend/previous_restart_time')
1620 if rst:
1621 rst = float(rst)
1622 timeout = now - rst
1623 if timeout < MINIMUM_RESTART_TIME:
1624 log.error(
1625 'VM %s restarting too fast (%f seconds since the last '
1626 'restart). Refusing to restart to avoid loops.',
1627 self.info['name'], timeout)
1628 self.destroy()
1629 return
1631 self.writeVm('xend/previous_restart_time', str(now))
1633 try:
1634 if rename:
1635 self.preserveForRestart()
1636 else:
1637 self.unwatchVm()
1638 self.destroyDomain()
1640 # new_dom's VM will be the same as this domain's VM, except where
1641 # the rename flag has instructed us to call preserveForRestart.
1642 # In that case, it is important that we remove the
1643 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1644 # once the new one is available.
1646 new_dom = None
1647 try:
1648 new_dom = XendDomain.instance().domain_create(config)
1649 new_dom.unpause()
1650 rst_cnt = self.readVm('xend/restart_count')
1651 rst_cnt = int(rst_cnt) + 1
1652 self.writeVm('xend/restart_count', str(rst_cnt))
1653 new_dom.removeVm(RESTART_IN_PROGRESS)
1654 except:
1655 if new_dom:
1656 new_dom.removeVm(RESTART_IN_PROGRESS)
1657 new_dom.destroy()
1658 else:
1659 self.removeVm(RESTART_IN_PROGRESS)
1660 raise
1661 except:
1662 log.exception('Failed to restart domain %d.', self.domid)
1665 def preserveForRestart(self):
1666 """Preserve a domain that has been shut down, by giving it a new UUID,
1667 cloning the VM details, and giving it a new name. This allows us to
1668 keep this domain for debugging, but restart a new one in its place
1669 preserving the restart semantics (name and UUID preserved).
1670 """
1672 new_name = self.generateUniqueName()
1673 new_uuid = uuid.toString(uuid.create())
1674 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1675 self.info['name'], self.domid, self.info['uuid'],
1676 new_name, new_uuid)
1677 self.unwatchVm()
1678 self.release_devices()
1679 self.info['name'] = new_name
1680 self.info['uuid'] = new_uuid
1681 self.vmpath = XendDomain.VMROOT + new_uuid
1682 self.storeVmDetails()
1683 self.preserve()
1686 def preserve(self):
1687 log.info("Preserving dead domain %s (%d).", self.info['name'],
1688 self.domid)
1689 self.unwatchVm()
1690 self.storeDom('xend/shutdown_completed', 'True')
1691 self.state_set(STATE_DOM_SHUTDOWN)
1694 # private:
1696 def generateUniqueName(self):
1697 n = 1
1698 while True:
1699 name = "%s-%d" % (self.info['name'], n)
1700 try:
1701 self.check_name(name)
1702 return name
1703 except VmError:
1704 n += 1
1707 def configure_bootloader(self):
1708 """Run the bootloader if we're configured to do so."""
1709 if not self.info['bootloader']:
1710 return
1711 blcfg = None
1712 # FIXME: this assumes that we want to use the first disk device
1713 for (n,c) in self.info['device']:
1714 if not n or not c or n != "vbd":
1715 continue
1716 disk = sxp.child_value(c, "uname")
1717 if disk is None:
1718 continue
1719 fn = blkdev_uname_to_file(disk)
1720 blcfg = bootloader(self.info['bootloader'], fn, 1,
1721 self.info['bootloader_args'],
1722 self.info['image'])
1723 break
1724 if blcfg is None:
1725 msg = "Had a bootloader specified, but can't find disk"
1726 log.error(msg)
1727 raise VmError(msg)
1728 self.info['image'] = blcfg
1731 def send_sysrq(self, key):
1732 asserts.isCharConvertible(key)
1734 self.storeDom("control/sysrq", '%c' % key)
1737 def infoIsSet(self, name):
1738 return name in self.info and self.info[name] is not None
1741 #============================================================================
1742 # Register device controllers and their device config types.
1744 """A map from device-class names to the subclass of DevController that
1745 implements the device control specific to that device-class."""
1746 controllerClasses = {}
1748 def addControllerClass(device_class, cls):
1749 """Register a subclass of DevController to handle the named device-class.
1750 """
1751 cls.deviceClass = device_class
1752 controllerClasses[device_class] = cls
1755 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1756 from xen.xend.server.BlktapController import BlktapController
1757 addControllerClass('vbd', blkif.BlkifController)
1758 addControllerClass('vif', netif.NetifController)
1759 addControllerClass('vtpm', tpmif.TPMifController)
1760 addControllerClass('pci', pciif.PciController)
1761 addControllerClass('ioports', iopif.IOPortsController)
1762 addControllerClass('irq', irqif.IRQController)
1763 addControllerClass('usb', usbif.UsbifController)
1764 addControllerClass('tap', BlktapController)