ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 11478:30c659b655f1

This patch add localtime to dumpcore filename.
And it add '-imcomplete' if failed.

Signed-off-by: Akio Takebe <takebe_akio@jp.fujitsu.com>
author root@procyon
date Thu Sep 14 08:19:39 2006 +0100 (2006-09-14)
parents 0008fca70351
children fe5c178cdf2e
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
52 from xen.xend import arch
54 """Shutdown code for poweroff."""
55 DOMAIN_POWEROFF = 0
57 """Shutdown code for reboot."""
58 DOMAIN_REBOOT = 1
60 """Shutdown code for suspend."""
61 DOMAIN_SUSPEND = 2
63 """Shutdown code for crash."""
64 DOMAIN_CRASH = 3
66 """Shutdown code for halt."""
67 DOMAIN_HALT = 4
69 """Map shutdown codes to strings."""
70 shutdown_reasons = {
71 DOMAIN_POWEROFF: "poweroff",
72 DOMAIN_REBOOT : "reboot",
73 DOMAIN_SUSPEND : "suspend",
74 DOMAIN_CRASH : "crash",
75 DOMAIN_HALT : "halt"
76 }
78 restart_modes = [
79 "restart",
80 "destroy",
81 "preserve",
82 "rename-restart"
83 ]
85 STATE_DOM_OK = 1
86 STATE_DOM_SHUTDOWN = 2
88 SHUTDOWN_TIMEOUT = 30.0
90 ZOMBIE_PREFIX = 'Zombie-'
92 """Constants for the different stages of ext. device migration """
93 DEV_MIGRATE_TEST = 0
94 DEV_MIGRATE_STEP1 = 1
95 DEV_MIGRATE_STEP2 = 2
96 DEV_MIGRATE_STEP3 = 3
98 """Minimum time between domain restarts in seconds."""
99 MINIMUM_RESTART_TIME = 20
101 RESTART_IN_PROGRESS = 'xend/restart_in_progress'
104 xc = xen.lowlevel.xc.xc()
105 xroot = XendRoot.instance()
107 log = logging.getLogger("xend.XendDomainInfo")
108 #log.setLevel(logging.TRACE)
111 ##
112 # All parameters of VMs that may be configured on-the-fly, or at start-up.
113 #
114 VM_CONFIG_PARAMS = [
115 ('name', str),
116 ('on_poweroff', str),
117 ('on_reboot', str),
118 ('on_crash', str),
119 ]
122 ##
123 # Configuration entries that we expect to round-trip -- be read from the
124 # config file or xc, written to save-files (i.e. through sxpr), and reused as
125 # config on restart or restore, all without munging. Some configuration
126 # entries are munged for backwards compatibility reasons, or because they
127 # don't come out of xc in the same form as they are specified in the config
128 # file, so those are handled separately.
129 ROUNDTRIPPING_CONFIG_ENTRIES = [
130 ('uuid', str),
131 ('vcpus', int),
132 ('vcpu_avail', int),
133 ('cpu_weight', float),
134 ('memory', int),
135 ('shadow_memory', int),
136 ('maxmem', int),
137 ('bootloader', str),
138 ('bootloader_args', str),
139 ('features', str),
140 ('localtime', int),
141 ]
143 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
146 ##
147 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
148 # entries written to the store that cannot be reconfigured on-the-fly.
149 #
150 VM_STORE_ENTRIES = [
151 ('uuid', str),
152 ('vcpus', int),
153 ('vcpu_avail', int),
154 ('memory', int),
155 ('shadow_memory', int),
156 ('maxmem', int),
157 ('start_time', float),
158 ]
160 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
163 #
164 # There are a number of CPU-related fields:
165 #
166 # vcpus: the number of virtual CPUs this domain is configured to use.
167 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
168 # its VCPUs. This is translated to
169 # <dompath>/cpu/<id>/availability = {online,offline} for use
170 # by the guest domain.
171 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
172 # CPUs that that VCPU may use.
173 # cpu: a configuration setting requesting that VCPU 0 is pinned to
174 # the specified physical CPU.
175 #
176 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
177 # across save, restore, migrate, and restart). The other settings are only
178 # specific to the domain, so are lost when the VM moves.
179 #
182 def create(config):
183 """Create a VM from a configuration.
185 @param config configuration
186 @raise: VmError for invalid configuration
187 """
189 log.debug("XendDomainInfo.create(%s)", config)
191 vm = XendDomainInfo(parseConfig(config))
192 try:
193 vm.construct()
194 vm.initDomain()
195 vm.storeVmDetails()
196 vm.storeDomDetails()
197 vm.registerWatches()
198 vm.refreshShutdown()
199 return vm
200 except:
201 log.exception('Domain construction failed')
202 vm.destroy()
203 raise
206 def recreate(xeninfo, priv):
207 """Create the VM object for an existing domain. The domain must not
208 be dying, as the paths in the store should already have been removed,
209 and asking us to recreate them causes problems."""
211 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
213 assert not xeninfo['dying']
215 domid = xeninfo['dom']
216 uuid1 = xeninfo['handle']
217 xeninfo['uuid'] = uuid.toString(uuid1)
218 dompath = GetDomainPath(domid)
219 if not dompath:
220 raise XendError(
221 'No domain path in store for existing domain %d' % domid)
223 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
224 try:
225 vmpath = xstransact.Read(dompath, "vm")
226 if not vmpath:
227 raise XendError(
228 'No vm path in store for existing domain %d' % domid)
229 uuid2_str = xstransact.Read(vmpath, "uuid")
230 if not uuid2_str:
231 raise XendError(
232 'No vm/uuid path in store for existing domain %d' % domid)
234 uuid2 = uuid.fromString(uuid2_str)
236 if uuid1 != uuid2:
237 raise XendError(
238 'Uuid in store does not match uuid for existing domain %d: '
239 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
241 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
243 except Exception, exn:
244 if priv:
245 log.warn(str(exn))
247 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
248 vm.recreateDom()
249 vm.removeVm()
250 vm.storeVmDetails()
251 vm.storeDomDetails()
253 vm.registerWatches()
254 vm.refreshShutdown(xeninfo)
255 return vm
258 def restore(config):
259 """Create a domain and a VM object to do a restore.
261 @param config: domain configuration
262 """
264 log.debug("XendDomainInfo.restore(%s)", config)
266 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
267 try:
268 vm.construct()
269 vm.storeVmDetails()
270 vm.createDevices()
271 vm.createChannels()
272 vm.storeDomDetails()
273 vm.endRestore()
274 return vm
275 except:
276 vm.destroy()
277 raise
280 def parseConfig(config):
281 def get_cfg(name, conv = None):
282 val = sxp.child_value(config, name)
284 if conv and not val is None:
285 try:
286 return conv(val)
287 except TypeError, exn:
288 raise VmError(
289 'Invalid setting %s = %s in configuration: %s' %
290 (name, val, str(exn)))
291 else:
292 return val
295 log.debug("parseConfig: config is %s", config)
297 result = {}
299 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
300 result[e[0]] = get_cfg(e[0], e[1])
302 result['cpu'] = get_cfg('cpu', int)
303 result['cpus'] = get_cfg('cpus', str)
304 result['image'] = get_cfg('image')
305 tmp_security = get_cfg('security')
306 if tmp_security:
307 result['security'] = tmp_security
309 try:
310 if result['image']:
311 v = sxp.child_value(result['image'], 'vcpus')
312 if result['vcpus'] is None and v is not None:
313 result['vcpus'] = int(v)
314 elif v is not None and int(v) != result['vcpus']:
315 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
316 ' Using %s VCPUs for VM %s.') %
317 (result['vcpus'], v, result['uuid']))
318 result['vcpus'] = int(v)
319 except TypeError, exn:
320 raise VmError(
321 'Invalid configuration setting: vcpus = %s: %s' %
322 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
324 try:
325 # support legacy config files with 'cpu' parameter
326 # NB: prepending to list to support previous behavior
327 # where 'cpu' parameter pinned VCPU0.
328 if result['cpu']:
329 if result['cpus']:
330 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
331 else:
332 result['cpus'] = str(result['cpu'])
334 # convert 'cpus' string to list of ints
335 # 'cpus' supports a list of ranges (0-3), seperated by
336 # commas, and negation, (^1).
337 # Precedence is settled by order of the string:
338 # "0-3,^1" -> [0,2,3]
339 # "0-3,^1,1" -> [0,1,2,3]
340 if result['cpus']:
341 cpus = []
342 for c in result['cpus'].split(','):
343 if c.find('-') != -1:
344 (x,y) = c.split('-')
345 for i in range(int(x),int(y)+1):
346 cpus.append(int(i))
347 else:
348 # remove this element from the list
349 if c[0] == '^':
350 cpus = [x for x in cpus if x != int(c[1:])]
351 else:
352 cpus.append(int(c))
354 result['cpus'] = cpus
356 except ValueError, exn:
357 raise VmError(
358 'Invalid configuration setting: cpus = %s: %s' %
359 (result['cpus'], exn))
361 result['backend'] = []
362 for c in sxp.children(config, 'backend'):
363 result['backend'].append(sxp.name(sxp.child0(c)))
365 result['device'] = []
366 for d in sxp.children(config, 'device'):
367 c = sxp.child0(d)
368 result['device'].append((sxp.name(c), c))
370 # Configuration option "restart" is deprecated. Parse it, but
371 # let on_xyz override it if they are present.
372 restart = get_cfg('restart')
373 if restart:
374 def handle_restart(event, val):
375 if result[event] is None:
376 result[event] = val
378 if restart == "onreboot":
379 handle_restart('on_poweroff', 'destroy')
380 handle_restart('on_reboot', 'restart')
381 handle_restart('on_crash', 'destroy')
382 elif restart == "always":
383 handle_restart('on_poweroff', 'restart')
384 handle_restart('on_reboot', 'restart')
385 handle_restart('on_crash', 'restart')
386 elif restart == "never":
387 handle_restart('on_poweroff', 'destroy')
388 handle_restart('on_reboot', 'destroy')
389 handle_restart('on_crash', 'destroy')
390 else:
391 log.warn("Ignoring malformed and deprecated config option "
392 "restart = %s", restart)
394 log.debug("parseConfig: result is %s", result)
395 return result
398 def domain_by_name(name):
399 return XendDomain.instance().domain_lookup_by_name_nr(name)
402 def shutdown_reason(code):
403 """Get a shutdown reason from a code.
405 @param code: shutdown code
406 @type code: int
407 @return: shutdown reason
408 @rtype: string
409 """
410 return shutdown_reasons.get(code, "?")
412 def dom_get(dom):
413 """Get info from xen for an existing domain.
415 @param dom: domain id
416 @return: info or None
417 """
418 try:
419 domlist = xc.domain_getinfo(dom, 1)
420 if domlist and dom == domlist[0]['dom']:
421 return domlist[0]
422 except Exception, err:
423 # ignore missing domain
424 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
425 return None
428 class XendDomainInfo:
430 def __init__(self, info, domid = None, dompath = None, augment = False,
431 priv = False, resume = False):
433 self.info = info
435 if not self.infoIsSet('uuid'):
436 self.info['uuid'] = uuid.toString(uuid.create())
438 if domid is not None:
439 self.domid = domid
440 elif 'dom' in info:
441 self.domid = int(info['dom'])
442 else:
443 self.domid = None
445 self.vmpath = XendDomain.VMROOT + self.info['uuid']
446 self.dompath = dompath
448 if augment:
449 self.augmentInfo(priv)
451 self.validateInfo()
453 self.image = None
454 self.security = None
455 self.store_port = None
456 self.store_mfn = None
457 self.console_port = None
458 self.console_mfn = None
460 self.vmWatch = None
461 self.shutdownWatch = None
463 self.shutdownStartTime = None
465 self.state = STATE_DOM_OK
466 self.state_updated = threading.Condition()
467 self.refresh_shutdown_lock = threading.Condition()
469 self.setResume(resume)
471 ## private:
473 def readVMDetails(self, params):
474 """Read the specified parameters from the store.
475 """
476 try:
477 return self.gatherVm(*params)
478 except ValueError:
479 # One of the int/float entries in params has a corresponding store
480 # entry that is invalid. We recover, because older versions of
481 # Xend may have put the entry there (memory/target, for example),
482 # but this is in general a bad situation to have reached.
483 log.exception(
484 "Store corrupted at %s! Domain %d's configuration may be "
485 "affected.", self.vmpath, self.domid)
486 return []
489 def storeChanged(self, _):
490 log.trace("XendDomainInfo.storeChanged");
492 changed = False
494 def f(x, y):
495 if y is not None and self.info[x[0]] != y:
496 self.info[x[0]] = y
497 changed = True
499 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
501 im = self.readVm('image')
502 current_im = self.info['image']
503 if (im is not None and
504 (current_im is None or sxp.to_string(current_im) != im)):
505 self.info['image'] = sxp.from_string(im)
506 changed = True
508 if changed:
509 # Update the domain section of the store, as this contains some
510 # parameters derived from the VM configuration.
511 self.storeDomDetails()
513 return 1
516 def augmentInfo(self, priv):
517 """Augment self.info, as given to us through {@link #recreate}, with
518 values taken from the store. This recovers those values known to xend
519 but not to the hypervisor.
520 """
521 def useIfNeeded(name, val):
522 if not self.infoIsSet(name) and val is not None:
523 self.info[name] = val
525 if priv:
526 entries = VM_STORE_ENTRIES[:]
527 entries.remove(('memory', int))
528 entries.remove(('maxmem', int))
529 else:
530 entries = VM_STORE_ENTRIES
531 entries.append(('image', str))
532 entries.append(('security', str))
534 map(lambda x, y: useIfNeeded(x[0], y), entries,
535 self.readVMDetails(entries))
537 device = []
538 for c in controllerClasses:
539 devconfig = self.getDeviceConfigurations(c)
540 if devconfig:
541 device.extend(map(lambda x: (c, x), devconfig))
542 useIfNeeded('device', device)
545 def validateInfo(self):
546 """Validate and normalise the info block. This has either been parsed
547 by parseConfig, or received from xc through recreate and augmented by
548 the current store contents.
549 """
550 def defaultInfo(name, val):
551 if not self.infoIsSet(name):
552 self.info[name] = val()
554 try:
555 defaultInfo('name', lambda: "Domain-%d" % self.domid)
556 defaultInfo('on_poweroff', lambda: "destroy")
557 defaultInfo('on_reboot', lambda: "restart")
558 defaultInfo('on_crash', lambda: "restart")
559 defaultInfo('features', lambda: "")
560 defaultInfo('cpu', lambda: None)
561 defaultInfo('cpus', lambda: [])
562 defaultInfo('cpu_weight', lambda: 1.0)
564 # some domains don't have a config file (e.g. dom0 )
565 # to set number of vcpus so we derive available cpus
566 # from max_vcpu_id which is present for running domains.
567 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
568 avail = int(self.info['max_vcpu_id'])+1
569 else:
570 avail = int(1)
572 defaultInfo('vcpus', lambda: avail)
573 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
574 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
575 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
577 defaultInfo('memory', lambda: 0)
578 defaultInfo('shadow_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'])
589 if isinstance(self.info['image'], str):
590 self.info['image'] = sxp.from_string(self.info['image'])
592 if isinstance(self.info['security'], str):
593 self.info['security'] = sxp.from_string(self.info['security'])
595 if self.info['memory'] == 0:
596 if self.infoIsSet('mem_kb'):
597 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
599 if self.info['maxmem'] < self.info['memory']:
600 self.info['maxmem'] = self.info['memory']
602 for (n, c) in self.info['device']:
603 if not n or not c or n not in controllerClasses:
604 raise VmError('invalid device (%s, %s)' %
605 (str(n), str(c)))
607 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
608 if self.info[event] not in restart_modes:
609 raise VmError('invalid restart event: %s = %s' %
610 (event, str(self.info[event])))
612 except KeyError, exn:
613 log.exception(exn)
614 raise VmError('Unspecified domain detail: %s' % exn)
617 def readVm(self, *args):
618 return xstransact.Read(self.vmpath, *args)
620 def writeVm(self, *args):
621 return xstransact.Write(self.vmpath, *args)
623 def removeVm(self, *args):
624 return xstransact.Remove(self.vmpath, *args)
626 def gatherVm(self, *args):
627 return xstransact.Gather(self.vmpath, *args)
630 ## public:
632 def storeVm(self, *args):
633 return xstransact.Store(self.vmpath, *args)
636 ## private:
638 def readDom(self, *args):
639 return xstransact.Read(self.dompath, *args)
641 def writeDom(self, *args):
642 return xstransact.Write(self.dompath, *args)
645 ## public:
647 def removeDom(self, *args):
648 return xstransact.Remove(self.dompath, *args)
650 def recreateDom(self):
651 complete(self.dompath, lambda t: self._recreateDom(t))
653 def _recreateDom(self, t):
654 t.remove()
655 t.mkdir()
656 t.set_permissions({ 'dom' : self.domid })
659 ## private:
661 def storeDom(self, *args):
662 return xstransact.Store(self.dompath, *args)
665 ## public:
667 def completeRestore(self, store_mfn, console_mfn):
669 log.debug("XendDomainInfo.completeRestore")
671 self.store_mfn = store_mfn
672 self.console_mfn = console_mfn
674 self.introduceDomain()
675 self.storeDomDetails()
676 self.registerWatches()
677 self.refreshShutdown()
679 log.debug("XendDomainInfo.completeRestore done")
682 def storeVmDetails(self):
683 to_store = {}
685 for k in VM_STORE_ENTRIES:
686 if self.infoIsSet(k[0]):
687 to_store[k[0]] = str(self.info[k[0]])
689 if self.infoIsSet('image'):
690 to_store['image'] = sxp.to_string(self.info['image'])
692 if self.infoIsSet('security'):
693 security = self.info['security']
694 to_store['security'] = sxp.to_string(security)
695 for idx in range(0, len(security)):
696 if security[idx][0] == 'access_control':
697 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
698 for aidx in range(1, len(security[idx])):
699 if security[idx][aidx][0] == 'label':
700 to_store['security/access_control/label'] = security[idx][aidx][1]
701 if security[idx][aidx][0] == 'policy':
702 to_store['security/access_control/policy'] = security[idx][aidx][1]
703 if security[idx][0] == 'ssidref':
704 to_store['security/ssidref'] = str(security[idx][1])
706 if not self.readVm('xend/restart_count'):
707 to_store['xend/restart_count'] = str(0)
709 log.debug("Storing VM details: %s", to_store)
711 self.writeVm(to_store)
712 self.setVmPermissions()
715 def setVmPermissions(self):
716 """Allow the guest domain to read its UUID. We don't allow it to
717 access any other entry, for security."""
718 xstransact.SetPermissions('%s/uuid' % self.vmpath,
719 { 'dom' : self.domid,
720 'read' : True,
721 'write' : False })
724 def storeDomDetails(self):
725 to_store = {
726 'domid': str(self.domid),
727 'vm': self.vmpath,
728 'name': self.info['name'],
729 'console/limit': str(xroot.get_console_limit() * 1024),
730 'memory/target': str(self.info['memory'] * 1024)
731 }
733 def f(n, v):
734 if v is not None:
735 to_store[n] = str(v)
737 f('console/port', self.console_port)
738 f('console/ring-ref', self.console_mfn)
739 f('store/port', self.store_port)
740 f('store/ring-ref', self.store_mfn)
742 to_store.update(self.vcpuDomDetails())
744 log.debug("Storing domain details: %s", to_store)
746 self.writeDom(to_store)
749 ## private:
751 def vcpuDomDetails(self):
752 def availability(n):
753 if self.info['vcpu_avail'] & (1 << n):
754 return 'online'
755 else:
756 return 'offline'
758 result = {}
759 for v in range(0, self.info['vcpus']):
760 result["cpu/%d/availability" % v] = availability(v)
761 return result
764 ## public:
766 def registerWatches(self):
767 """Register a watch on this VM's entries in the store, and the
768 domain's control/shutdown node, so that when they are changed
769 externally, we keep up to date. This should only be called by {@link
770 #create}, {@link #recreate}, or {@link #restore}, once the domain's
771 details have been written, but before the new instance is returned."""
772 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
773 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
774 self.handleShutdownWatch)
777 def getDomid(self):
778 return self.domid
780 def setName(self, name):
781 self.check_name(name)
782 self.info['name'] = name
783 self.storeVm("name", name)
785 def getName(self):
786 return self.info['name']
788 def getDomainPath(self):
789 return self.dompath
792 def getStorePort(self):
793 """For use only by image.py and XendCheckpoint.py."""
794 return self.store_port
797 def getConsolePort(self):
798 """For use only by image.py and XendCheckpoint.py"""
799 return self.console_port
801 def getFeatures(self):
802 """For use only by image.py."""
803 return self.info['features']
805 def getVCpuCount(self):
806 return self.info['vcpus']
809 def setVCpuCount(self, vcpus):
810 self.info['vcpu_avail'] = (1 << vcpus) - 1
811 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
812 self.writeDom(self.vcpuDomDetails())
814 def getLabel(self):
815 return security.get_security_info(self.info, 'label')
817 def getMemoryTarget(self):
818 """Get this domain's target memory size, in KB."""
819 return self.info['memory'] * 1024
821 def getResume(self):
822 return "%s" % self.info['resume']
824 def endRestore(self):
825 self.setResume(False)
827 def setResume(self, state):
828 self.info['resume'] = state
830 def getRestartCount(self):
831 return self.readVm('xend/restart_count')
833 def refreshShutdown(self, xeninfo = None):
834 # If set at the end of this method, a restart is required, with the
835 # given reason. This restart has to be done out of the scope of
836 # refresh_shutdown_lock.
837 restart_reason = None
839 self.refresh_shutdown_lock.acquire()
840 try:
841 if xeninfo is None:
842 xeninfo = dom_get(self.domid)
843 if xeninfo is None:
844 # The domain no longer exists. This will occur if we have
845 # scheduled a timer to check for shutdown timeouts and the
846 # shutdown succeeded. It will also occur if someone
847 # destroys a domain beneath us. We clean up the domain,
848 # just in case, but we can't clean up the VM, because that
849 # VM may have migrated to a different domain on this
850 # machine.
851 self.cleanupDomain()
852 return
854 if xeninfo['dying']:
855 # Dying means that a domain has been destroyed, but has not
856 # yet been cleaned up by Xen. This state could persist
857 # indefinitely if, for example, another domain has some of its
858 # pages mapped. We might like to diagnose this problem in the
859 # future, but for now all we do is make sure that it's not us
860 # holding the pages, by calling cleanupDomain. We can't
861 # clean up the VM, as above.
862 self.cleanupDomain()
863 return
865 elif xeninfo['crashed']:
866 if self.readDom('xend/shutdown_completed'):
867 # We've seen this shutdown already, but we are preserving
868 # the domain for debugging. Leave it alone.
869 return
871 log.warn('Domain has crashed: name=%s id=%d.',
872 self.info['name'], self.domid)
874 if xroot.get_enable_dump():
875 self.dumpCore()
877 restart_reason = 'crash'
879 elif xeninfo['shutdown']:
880 if self.readDom('xend/shutdown_completed'):
881 # We've seen this shutdown already, but we are preserving
882 # the domain for debugging. Leave it alone.
883 return
885 else:
886 reason = shutdown_reason(xeninfo['shutdown_reason'])
888 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
889 self.info['name'], self.domid, reason)
891 self.clearRestart()
893 if reason == 'suspend':
894 self.state_set(STATE_DOM_SHUTDOWN)
895 # Don't destroy the domain. XendCheckpoint will do
896 # this once it has finished. However, stop watching
897 # the VM path now, otherwise we will end up with one
898 # watch for the old domain, and one for the new.
899 self.unwatchVm()
900 elif reason in ['poweroff', 'reboot']:
901 restart_reason = reason
902 else:
903 self.destroy()
905 elif self.dompath is None:
906 # We have yet to manage to call introduceDomain on this
907 # domain. This can happen if a restore is in progress, or has
908 # failed. Ignore this domain.
909 pass
910 else:
911 # Domain is alive. If we are shutting it down, then check
912 # the timeout on that, and destroy it if necessary.
914 if self.shutdownStartTime:
915 timeout = (SHUTDOWN_TIMEOUT - time.time() +
916 self.shutdownStartTime)
917 if timeout < 0:
918 log.info(
919 "Domain shutdown timeout expired: name=%s id=%s",
920 self.info['name'], self.domid)
921 self.destroy()
922 finally:
923 self.refresh_shutdown_lock.release()
925 if restart_reason:
926 self.maybeRestart(restart_reason)
929 def handleShutdownWatch(self, _):
930 log.debug('XendDomainInfo.handleShutdownWatch')
932 reason = self.readDom('control/shutdown')
934 if reason and reason != 'suspend':
935 sst = self.readDom('xend/shutdown_start_time')
936 now = time.time()
937 if sst:
938 self.shutdownStartTime = float(sst)
939 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
940 else:
941 self.shutdownStartTime = now
942 self.storeDom('xend/shutdown_start_time', now)
943 timeout = SHUTDOWN_TIMEOUT
945 log.trace(
946 "Scheduling refreshShutdown on domain %d in %ds.",
947 self.domid, timeout)
948 threading.Timer(timeout, self.refreshShutdown).start()
950 return True
953 def shutdown(self, reason):
954 if not reason in shutdown_reasons.values():
955 raise XendError('Invalid reason: %s' % reason)
956 if self.domid == 0:
957 raise XendError("Can't specify Domain-0")
958 self.storeDom("control/shutdown", reason)
961 ## private:
963 def clearRestart(self):
964 self.removeDom("xend/shutdown_start_time")
967 def maybeRestart(self, reason):
968 # Dispatch to the correct method based upon the configured on_{reason}
969 # behaviour.
970 {"destroy" : self.destroy,
971 "restart" : self.restart,
972 "preserve" : self.preserve,
973 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
976 def renameRestart(self):
977 self.restart(True)
980 def dumpCore(self,corefile=None):
981 """Create a core dump for this domain. Nothrow guarantee."""
983 try:
984 if not corefile:
985 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
986 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
987 self.info['name'], self.domid)
988 xc.domain_dumpcore(self.domid, corefile)
990 except:
991 corefile_incomp = corefile+'-incomplete'
992 os.rename(corefile, corefile_incomp)
993 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
994 self.domid, self.info['name'])
997 ## public:
999 def setMemoryTarget(self, target):
1000 """Set the memory target of this domain.
1001 @param target In MiB.
1002 """
1003 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
1004 self.info['name'], self.domid, target)
1006 self.info['memory'] = target
1007 self.storeVm("memory", target)
1008 self.storeDom("memory/target", target << 10)
1011 def update(self, info = None):
1012 """Update with info from xc.domain_getinfo().
1013 """
1015 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1016 if not info:
1017 info = dom_get(self.domid)
1018 if not info:
1019 return
1021 #manually update ssidref / security fields
1022 if security.on() and info.has_key('ssidref'):
1023 if (info['ssidref'] != 0) and self.info.has_key('security'):
1024 security_field = self.info['security']
1025 if not security_field:
1026 #create new security element
1027 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1028 #ssidref field not used any longer
1029 info.pop('ssidref')
1031 self.info.update(info)
1032 self.validateInfo()
1033 self.refreshShutdown(info)
1035 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1036 self.info)
1039 ## private:
1041 def state_set(self, state):
1042 self.state_updated.acquire()
1043 try:
1044 if self.state != state:
1045 self.state = state
1046 self.state_updated.notifyAll()
1047 finally:
1048 self.state_updated.release()
1051 ## public:
1053 def waitForShutdown(self):
1054 self.state_updated.acquire()
1055 try:
1056 while self.state == STATE_DOM_OK:
1057 self.state_updated.wait()
1058 finally:
1059 self.state_updated.release()
1062 def __str__(self):
1063 s = "<domain"
1064 s += " id=" + str(self.domid)
1065 s += " name=" + self.info['name']
1066 s += " memory=" + str(self.info['memory'])
1067 s += ">"
1068 return s
1070 __repr__ = __str__
1073 ## private:
1075 def createDevice(self, deviceClass, devconfig):
1076 return self.getDeviceController(deviceClass).createDevice(devconfig)
1079 def waitForDevices_(self, deviceClass):
1080 return self.getDeviceController(deviceClass).waitForDevices()
1083 def waitForDevice(self, deviceClass, devid):
1084 return self.getDeviceController(deviceClass).waitForDevice(devid)
1087 def reconfigureDevice(self, deviceClass, devid, devconfig):
1088 return self.getDeviceController(deviceClass).reconfigureDevice(
1089 devid, devconfig)
1092 ## public:
1094 def destroyDevice(self, deviceClass, devid):
1095 if type(devid) is str:
1096 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
1097 for entry in xstransact.List(devicePath):
1098 backend = xstransact.Read('%s/%s' % (devicePath, entry), "backend")
1099 devName = xstransact.Read(backend, "dev")
1100 if devName == devid:
1101 # We found the integer matching our devid, use it instead
1102 devid = entry
1103 break
1104 return self.getDeviceController(deviceClass).destroyDevice(devid)
1107 def getDeviceSxprs(self, deviceClass):
1108 return self.getDeviceController(deviceClass).sxprs()
1111 ## private:
1113 def getDeviceConfigurations(self, deviceClass):
1114 return self.getDeviceController(deviceClass).configurations()
1117 def getDeviceController(self, name):
1118 if name not in controllerClasses:
1119 raise XendError("unknown device type: " + str(name))
1121 return controllerClasses[name](self)
1124 ## public:
1126 def sxpr(self):
1127 sxpr = ['domain',
1128 ['domid', self.domid]]
1130 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1131 if self.infoIsSet(e[0]):
1132 sxpr.append([e[0], self.info[e[0]]])
1134 if self.infoIsSet('image'):
1135 sxpr.append(['image', self.info['image']])
1137 if self.infoIsSet('security'):
1138 sxpr.append(['security', self.info['security']])
1140 for cls in controllerClasses:
1141 for config in self.getDeviceConfigurations(cls):
1142 sxpr.append(['device', config])
1144 def stateChar(name):
1145 if name in self.info:
1146 if self.info[name]:
1147 return name[0]
1148 else:
1149 return '-'
1150 else:
1151 return '?'
1153 state = reduce(
1154 lambda x, y: x + y,
1155 map(stateChar,
1156 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1157 'dying']))
1159 sxpr.append(['state', state])
1160 if self.infoIsSet('shutdown'):
1161 reason = shutdown_reason(self.info['shutdown_reason'])
1162 sxpr.append(['shutdown_reason', reason])
1163 if self.infoIsSet('cpu_time'):
1164 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1165 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1167 if self.infoIsSet('start_time'):
1168 up_time = time.time() - self.info['start_time']
1169 sxpr.append(['up_time', str(up_time) ])
1170 sxpr.append(['start_time', str(self.info['start_time']) ])
1172 if self.store_mfn:
1173 sxpr.append(['store_mfn', self.store_mfn])
1174 if self.console_mfn:
1175 sxpr.append(['console_mfn', self.console_mfn])
1177 return sxpr
1180 def getVCPUInfo(self):
1181 try:
1182 # We include the domain name and ID, to help xm.
1183 sxpr = ['domain',
1184 ['domid', self.domid],
1185 ['name', self.info['name']],
1186 ['vcpu_count', self.info['online_vcpus']]]
1188 for i in range(0, self.info['max_vcpu_id']+1):
1189 info = xc.vcpu_getinfo(self.domid, i)
1191 sxpr.append(['vcpu',
1192 ['number', i],
1193 ['online', info['online']],
1194 ['blocked', info['blocked']],
1195 ['running', info['running']],
1196 ['cpu_time', info['cpu_time'] / 1e9],
1197 ['cpu', info['cpu']],
1198 ['cpumap', info['cpumap']]])
1200 return sxpr
1202 except RuntimeError, exn:
1203 raise XendError(str(exn))
1206 ## private:
1208 def check_name(self, name):
1209 """Check if a vm name is valid. Valid names contain alphabetic characters,
1210 digits, or characters in '_-.:/+'.
1211 The same name cannot be used for more than one vm at the same time.
1213 @param name: name
1214 @raise: VmError if invalid
1215 """
1216 if name is None or name == '':
1217 raise VmError('missing vm name')
1218 for c in name:
1219 if c in string.digits: continue
1220 if c in '_-.:/+': continue
1221 if c in string.ascii_letters: continue
1222 raise VmError('invalid vm name')
1224 dominfo = domain_by_name(name)
1225 if not dominfo:
1226 return
1227 if self.domid is None:
1228 raise VmError("VM name '%s' already in use by domain %d" %
1229 (name, dominfo.domid))
1230 if dominfo.domid != self.domid:
1231 raise VmError("VM name '%s' is used in both domains %d and %d" %
1232 (name, 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 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1299 for v in range(0, self.info['max_vcpu_id']+1):
1300 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1302 # Use architecture- and image-specific calculations to determine
1303 # the various headrooms necessary, given the raw configured
1304 # values.
1305 # reservation, maxmem, memory, and shadow are all in KiB.
1306 reservation = self.image.getRequiredInitialReservation(
1307 self.info['memory'] * 1024)
1308 maxmem = self.image.getRequiredAvailableMemory(
1309 self.info['maxmem'] * 1024)
1310 memory = self.image.getRequiredAvailableMemory(
1311 self.info['memory'] * 1024)
1312 shadow = self.image.getRequiredShadowMemory(
1313 self.info['shadow_memory'] * 1024,
1314 self.info['maxmem'] * 1024)
1316 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1317 # takes MiB and we must not round down and end up under-providing.
1318 shadow = ((shadow + 1023) / 1024) * 1024
1320 # set memory limit
1321 xc.domain_setmaxmem(self.domid, maxmem)
1323 # Make sure there's enough RAM available for the domain
1324 balloon.free(memory + shadow)
1326 # Set up the shadow memory
1327 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1328 self.info['shadow_memory'] = shadow_cur
1330 # initial memory reservation
1331 xc.domain_memory_increase_reservation(self.domid, reservation, 0,
1332 0)
1334 self.createChannels()
1336 channel_details = self.image.createImage()
1338 self.store_mfn = channel_details['store_mfn']
1339 if 'console_mfn' in channel_details:
1340 self.console_mfn = channel_details['console_mfn']
1342 self.introduceDomain()
1344 self.createDevices()
1346 if self.info['bootloader']:
1347 self.image.cleanupBootloading()
1349 self.info['start_time'] = time.time()
1351 except RuntimeError, exn:
1352 raise VmError(str(exn))
1355 ## public:
1357 def cleanupDomain(self):
1358 """Cleanup domain resources; release devices. Idempotent. Nothrow
1359 guarantee."""
1361 self.refresh_shutdown_lock.acquire()
1362 try:
1363 self.unwatchShutdown()
1365 self.release_devices()
1367 if self.image:
1368 try:
1369 self.image.destroy()
1370 except:
1371 log.exception(
1372 "XendDomainInfo.cleanup: image.destroy() failed.")
1373 self.image = None
1375 try:
1376 self.removeDom()
1377 except:
1378 log.exception("Removing domain path failed.")
1380 try:
1381 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1382 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1383 except:
1384 log.exception("Renaming Zombie failed.")
1386 self.state_set(STATE_DOM_SHUTDOWN)
1387 finally:
1388 self.refresh_shutdown_lock.release()
1391 def cleanupVm(self):
1392 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1394 self.unwatchVm()
1396 try:
1397 self.removeVm()
1398 except:
1399 log.exception("Removing VM path failed.")
1402 ## private:
1404 def unwatchVm(self):
1405 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1406 guarantee."""
1408 try:
1409 try:
1410 if self.vmWatch:
1411 self.vmWatch.unwatch()
1412 finally:
1413 self.vmWatch = None
1414 except:
1415 log.exception("Unwatching VM path failed.")
1418 def unwatchShutdown(self):
1419 """Remove the watch on the domain's control/shutdown node, if any.
1420 Idempotent. Nothrow guarantee. Expects to be protected by the
1421 refresh_shutdown_lock."""
1423 try:
1424 try:
1425 if self.shutdownWatch:
1426 self.shutdownWatch.unwatch()
1427 finally:
1428 self.shutdownWatch = None
1429 except:
1430 log.exception("Unwatching control/shutdown failed.")
1433 ## public:
1435 def destroy(self):
1436 """Cleanup VM and destroy domain. Nothrow guarantee."""
1438 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1440 self.cleanupVm()
1441 if self.dompath is not None:
1442 self.destroyDomain()
1445 def destroyDomain(self):
1446 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1448 try:
1449 if self.domid is not None:
1450 xc.domain_destroy(self.domid)
1451 except:
1452 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1454 self.cleanupDomain()
1457 ## private:
1459 def release_devices(self):
1460 """Release all domain's devices. Nothrow guarantee."""
1462 while True:
1463 t = xstransact("%s/device" % self.dompath)
1464 for n in controllerClasses.keys():
1465 for d in t.list(n):
1466 try:
1467 t.remove(d)
1468 except:
1469 # Log and swallow any exceptions in removal --
1470 # there's nothing more we can do.
1471 log.exception(
1472 "Device release failed: %s; %s; %s",
1473 self.info['name'], n, d)
1474 if t.commit():
1475 break
1478 def createChannels(self):
1479 """Create the channels to the domain.
1480 """
1481 self.store_port = self.createChannel()
1482 self.console_port = self.createChannel()
1485 def createChannel(self):
1486 """Create an event channel to the domain.
1487 """
1488 try:
1489 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1490 except:
1491 log.exception("Exception in alloc_unbound(%d)", self.domid)
1492 raise
1495 ## public:
1497 def createDevices(self):
1498 """Create the devices for a vm.
1500 @raise: VmError for invalid devices
1501 """
1503 for (n, c) in self.info['device']:
1504 self.createDevice(n, c)
1506 if self.image:
1507 self.image.createDeviceModel()
1509 ## public:
1511 def checkLiveMigrateMemory(self):
1512 """ Make sure there's enough memory to migrate this domain """
1513 overhead_kb = 0
1514 if arch.type == "x86":
1515 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1516 # the minimum that Xen would allocate if no value were given.
1517 overhead_kb = self.info['vcpus'] * 1024 + self.info['maxmem'] * 4
1518 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1519 # The domain might already have some shadow memory
1520 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1521 if overhead_kb > 0:
1522 balloon.free(overhead_kb)
1524 def testMigrateDevices(self, network, dst):
1525 """ Notify all device about intention of migration
1526 @raise: XendError for a device that cannot be migrated
1527 """
1528 for (n, c) in self.info['device']:
1529 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1530 if rc != 0:
1531 raise XendError("Device of type '%s' refuses migration." % n)
1533 def testDeviceComplete(self):
1534 """ For Block IO migration safety we must ensure that
1535 the device has shutdown correctly, i.e. all blocks are
1536 flushed to disk
1537 """
1538 while True:
1539 test = 0
1540 for i in self.getDeviceController('vbd').deviceIDs():
1541 test = 1
1542 log.info("Dev %s still active, looping...", i)
1543 time.sleep(0.1)
1545 if test == 0:
1546 break
1548 def migrateDevices(self, network, dst, step, domName=''):
1549 """Notify the devices about migration
1550 """
1551 ctr = 0
1552 try:
1553 for (n, c) in self.info['device']:
1554 self.migrateDevice(n, c, network, dst, step, domName)
1555 ctr = ctr + 1
1556 except:
1557 for (n, c) in self.info['device']:
1558 if ctr == 0:
1559 step = step - 1
1560 ctr = ctr - 1
1561 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1562 raise
1564 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1565 step, domName=''):
1566 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1567 network, dst, step, domName)
1569 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1570 dst, step, domName=''):
1571 return self.getDeviceController(deviceClass).recover_migrate(
1572 deviceConfig, network, dst, step, domName)
1574 def waitForDevices(self):
1575 """Wait for this domain's configured devices to connect.
1577 @raise: VmError if any device fails to initialise.
1578 """
1579 for c in controllerClasses:
1580 self.waitForDevices_(c)
1583 def device_create(self, dev_config):
1584 """Create a new device.
1586 @param dev_config: device configuration
1587 """
1588 dev_type = sxp.name(dev_config)
1589 devid = self.createDevice(dev_type, dev_config)
1590 self.waitForDevice(dev_type, devid)
1591 self.info['device'].append((dev_type, dev_config))
1592 return self.getDeviceController(dev_type).sxpr(devid)
1595 def device_configure(self, dev_config):
1596 """Configure an existing device.
1597 @param dev_config: device configuration
1598 """
1599 deviceClass = sxp.name(dev_config)
1600 self.reconfigureDevice(deviceClass, None, dev_config)
1603 def pause(self):
1604 xc.domain_pause(self.domid)
1607 def unpause(self):
1608 xc.domain_unpause(self.domid)
1611 ## private:
1613 def restart(self, rename = False):
1614 """Restart the domain after it has exited.
1616 @param rename True if the old domain is to be renamed and preserved,
1617 False if it is to be destroyed.
1618 """
1620 self.configure_bootloader()
1621 config = self.sxpr()
1623 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1624 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1625 self.info['cpus'])])
1627 if self.readVm(RESTART_IN_PROGRESS):
1628 log.error('Xend failed during restart of domain %d. '
1629 'Refusing to restart to avoid loops.',
1630 self.domid)
1631 self.destroy()
1632 return
1634 self.writeVm(RESTART_IN_PROGRESS, 'True')
1636 now = time.time()
1637 rst = self.readVm('xend/previous_restart_time')
1638 if rst:
1639 rst = float(rst)
1640 timeout = now - rst
1641 if timeout < MINIMUM_RESTART_TIME:
1642 log.error(
1643 'VM %s restarting too fast (%f seconds since the last '
1644 'restart). Refusing to restart to avoid loops.',
1645 self.info['name'], timeout)
1646 self.destroy()
1647 return
1649 self.writeVm('xend/previous_restart_time', str(now))
1651 try:
1652 if rename:
1653 self.preserveForRestart()
1654 else:
1655 self.unwatchVm()
1656 self.destroyDomain()
1658 # new_dom's VM will be the same as this domain's VM, except where
1659 # the rename flag has instructed us to call preserveForRestart.
1660 # In that case, it is important that we remove the
1661 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1662 # once the new one is available.
1664 new_dom = None
1665 try:
1666 new_dom = XendDomain.instance().domain_create(config)
1667 new_dom.unpause()
1668 rst_cnt = self.readVm('xend/restart_count')
1669 rst_cnt = int(rst_cnt) + 1
1670 self.writeVm('xend/restart_count', str(rst_cnt))
1671 new_dom.removeVm(RESTART_IN_PROGRESS)
1672 except:
1673 if new_dom:
1674 new_dom.removeVm(RESTART_IN_PROGRESS)
1675 new_dom.destroy()
1676 else:
1677 self.removeVm(RESTART_IN_PROGRESS)
1678 raise
1679 except:
1680 log.exception('Failed to restart domain %d.', self.domid)
1683 def preserveForRestart(self):
1684 """Preserve a domain that has been shut down, by giving it a new UUID,
1685 cloning the VM details, and giving it a new name. This allows us to
1686 keep this domain for debugging, but restart a new one in its place
1687 preserving the restart semantics (name and UUID preserved).
1688 """
1690 new_name = self.generateUniqueName()
1691 new_uuid = uuid.toString(uuid.create())
1692 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1693 self.info['name'], self.domid, self.info['uuid'],
1694 new_name, new_uuid)
1695 self.unwatchVm()
1696 self.release_devices()
1697 self.info['name'] = new_name
1698 self.info['uuid'] = new_uuid
1699 self.vmpath = XendDomain.VMROOT + new_uuid
1700 self.storeVmDetails()
1701 self.preserve()
1704 def preserve(self):
1705 log.info("Preserving dead domain %s (%d).", self.info['name'],
1706 self.domid)
1707 self.unwatchVm()
1708 self.storeDom('xend/shutdown_completed', 'True')
1709 self.state_set(STATE_DOM_SHUTDOWN)
1712 # private:
1714 def generateUniqueName(self):
1715 n = 1
1716 while True:
1717 name = "%s-%d" % (self.info['name'], n)
1718 try:
1719 self.check_name(name)
1720 return name
1721 except VmError:
1722 n += 1
1725 def configure_bootloader(self):
1726 """Run the bootloader if we're configured to do so."""
1727 if not self.info['bootloader']:
1728 return
1729 blcfg = None
1730 # FIXME: this assumes that we want to use the first disk device
1731 for (n,c) in self.info['device']:
1732 if not n or not c or n != "vbd":
1733 continue
1734 disk = sxp.child_value(c, "uname")
1735 if disk is None:
1736 continue
1737 fn = blkdev_uname_to_file(disk)
1738 blcfg = bootloader(self.info['bootloader'], fn, 1,
1739 self.info['bootloader_args'],
1740 self.info['image'])
1741 break
1742 if blcfg is None:
1743 msg = "Had a bootloader specified, but can't find disk"
1744 log.error(msg)
1745 raise VmError(msg)
1746 self.info['image'] = blcfg
1749 def send_sysrq(self, key):
1750 asserts.isCharConvertible(key)
1752 self.storeDom("control/sysrq", '%c' % key)
1755 def infoIsSet(self, name):
1756 return name in self.info and self.info[name] is not None
1759 #============================================================================
1760 # Register device controllers and their device config types.
1762 """A map from device-class names to the subclass of DevController that
1763 implements the device control specific to that device-class."""
1764 controllerClasses = {}
1766 def addControllerClass(device_class, cls):
1767 """Register a subclass of DevController to handle the named device-class.
1768 """
1769 cls.deviceClass = device_class
1770 controllerClasses[device_class] = cls
1773 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1774 from xen.xend.server.BlktapController import BlktapController
1775 addControllerClass('vbd', blkif.BlkifController)
1776 addControllerClass('vif', netif.NetifController)
1777 addControllerClass('vtpm', tpmif.TPMifController)
1778 addControllerClass('pci', pciif.PciController)
1779 addControllerClass('ioports', iopif.IOPortsController)
1780 addControllerClass('irq', irqif.IRQController)
1781 addControllerClass('usb', usbif.UsbifController)
1782 addControllerClass('tap', BlktapController)