ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 11585:1db4e40b4d83

[XEND] Check for invalid memory sizes.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author atse@norwich.uk.xensource.com
date Fri Sep 22 15:36:11 2006 +0100 (2006-09-22)
parents fe5c178cdf2e
children 332bdaab35fa
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
89 MIGRATE_TIMEOUT = 30.0
91 ZOMBIE_PREFIX = 'Zombie-'
93 """Constants for the different stages of ext. device migration """
94 DEV_MIGRATE_TEST = 0
95 DEV_MIGRATE_STEP1 = 1
96 DEV_MIGRATE_STEP2 = 2
97 DEV_MIGRATE_STEP3 = 3
99 """Minimum time between domain restarts in seconds."""
100 MINIMUM_RESTART_TIME = 20
102 RESTART_IN_PROGRESS = 'xend/restart_in_progress'
105 xc = xen.lowlevel.xc.xc()
106 xroot = XendRoot.instance()
108 log = logging.getLogger("xend.XendDomainInfo")
109 #log.setLevel(logging.TRACE)
112 ##
113 # All parameters of VMs that may be configured on-the-fly, or at start-up.
114 #
115 VM_CONFIG_PARAMS = [
116 ('name', str),
117 ('on_poweroff', str),
118 ('on_reboot', str),
119 ('on_crash', str),
120 ]
123 ##
124 # Configuration entries that we expect to round-trip -- be read from the
125 # config file or xc, written to save-files (i.e. through sxpr), and reused as
126 # config on restart or restore, all without munging. Some configuration
127 # entries are munged for backwards compatibility reasons, or because they
128 # don't come out of xc in the same form as they are specified in the config
129 # file, so those are handled separately.
130 ROUNDTRIPPING_CONFIG_ENTRIES = [
131 ('uuid', str),
132 ('vcpus', int),
133 ('vcpu_avail', int),
134 ('cpu_weight', float),
135 ('memory', int),
136 ('shadow_memory', int),
137 ('maxmem', int),
138 ('bootloader', str),
139 ('bootloader_args', str),
140 ('features', str),
141 ('localtime', int),
142 ]
144 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
147 ##
148 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
149 # entries written to the store that cannot be reconfigured on-the-fly.
150 #
151 VM_STORE_ENTRIES = [
152 ('uuid', str),
153 ('vcpus', int),
154 ('vcpu_avail', int),
155 ('memory', int),
156 ('shadow_memory', int),
157 ('maxmem', int),
158 ('start_time', float),
159 ]
161 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
164 #
165 # There are a number of CPU-related fields:
166 #
167 # vcpus: the number of virtual CPUs this domain is configured to use.
168 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
169 # its VCPUs. This is translated to
170 # <dompath>/cpu/<id>/availability = {online,offline} for use
171 # by the guest domain.
172 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
173 # CPUs that that VCPU may use.
174 # cpu: a configuration setting requesting that VCPU 0 is pinned to
175 # the specified physical CPU.
176 #
177 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
178 # across save, restore, migrate, and restart). The other settings are only
179 # specific to the domain, so are lost when the VM moves.
180 #
183 def create(config):
184 """Create a VM from a configuration.
186 @param config configuration
187 @raise: VmError for invalid configuration
188 """
190 log.debug("XendDomainInfo.create(%s)", config)
192 vm = XendDomainInfo(parseConfig(config))
193 try:
194 vm.construct()
195 vm.initDomain()
196 vm.storeVmDetails()
197 vm.storeDomDetails()
198 vm.registerWatches()
199 vm.refreshShutdown()
200 return vm
201 except:
202 log.exception('Domain construction failed')
203 vm.destroy()
204 raise
207 def recreate(xeninfo, priv):
208 """Create the VM object for an existing domain. The domain must not
209 be dying, as the paths in the store should already have been removed,
210 and asking us to recreate them causes problems."""
212 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
214 assert not xeninfo['dying']
216 domid = xeninfo['dom']
217 uuid1 = xeninfo['handle']
218 xeninfo['uuid'] = uuid.toString(uuid1)
219 dompath = GetDomainPath(domid)
220 if not dompath:
221 raise XendError(
222 'No domain path in store for existing domain %d' % domid)
224 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
225 try:
226 vmpath = xstransact.Read(dompath, "vm")
227 if not vmpath:
228 raise XendError(
229 'No vm path in store for existing domain %d' % domid)
230 uuid2_str = xstransact.Read(vmpath, "uuid")
231 if not uuid2_str:
232 raise XendError(
233 'No vm/uuid path in store for existing domain %d' % domid)
235 uuid2 = uuid.fromString(uuid2_str)
237 if uuid1 != uuid2:
238 raise XendError(
239 'Uuid in store does not match uuid for existing domain %d: '
240 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
242 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
244 except Exception, exn:
245 if priv:
246 log.warn(str(exn))
248 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
249 vm.recreateDom()
250 vm.removeVm()
251 vm.storeVmDetails()
252 vm.storeDomDetails()
254 vm.registerWatches()
255 vm.refreshShutdown(xeninfo)
256 return vm
259 def restore(config):
260 """Create a domain and a VM object to do a restore.
262 @param config: domain configuration
263 """
265 log.debug("XendDomainInfo.restore(%s)", config)
267 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
268 try:
269 vm.construct()
270 vm.storeVmDetails()
271 vm.createDevices()
272 vm.createChannels()
273 vm.storeDomDetails()
274 vm.endRestore()
275 return vm
276 except:
277 vm.destroy()
278 raise
281 def parseConfig(config):
282 def get_cfg(name, conv = None):
283 val = sxp.child_value(config, name)
285 if conv and not val is None:
286 try:
287 return conv(val)
288 except TypeError, exn:
289 raise VmError(
290 'Invalid setting %s = %s in configuration: %s' %
291 (name, val, str(exn)))
292 else:
293 return val
296 log.debug("parseConfig: config is %s", config)
298 result = {}
300 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
301 result[e[0]] = get_cfg(e[0], e[1])
303 result['cpu'] = get_cfg('cpu', int)
304 result['cpus'] = get_cfg('cpus', str)
305 result['image'] = get_cfg('image')
306 tmp_security = get_cfg('security')
307 if tmp_security:
308 result['security'] = tmp_security
310 try:
311 if result['image']:
312 v = sxp.child_value(result['image'], 'vcpus')
313 if result['vcpus'] is None and v is not None:
314 result['vcpus'] = int(v)
315 elif v is not None and int(v) != result['vcpus']:
316 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
317 ' Using %s VCPUs for VM %s.') %
318 (result['vcpus'], v, result['uuid']))
319 result['vcpus'] = int(v)
320 except TypeError, exn:
321 raise VmError(
322 'Invalid configuration setting: vcpus = %s: %s' %
323 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
325 try:
326 # support legacy config files with 'cpu' parameter
327 # NB: prepending to list to support previous behavior
328 # where 'cpu' parameter pinned VCPU0.
329 if result['cpu']:
330 if result['cpus']:
331 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
332 else:
333 result['cpus'] = str(result['cpu'])
335 # convert 'cpus' string to list of ints
336 # 'cpus' supports a list of ranges (0-3), seperated by
337 # commas, and negation, (^1).
338 # Precedence is settled by order of the string:
339 # "0-3,^1" -> [0,2,3]
340 # "0-3,^1,1" -> [0,1,2,3]
341 if result['cpus']:
342 cpus = []
343 for c in result['cpus'].split(','):
344 if c.find('-') != -1:
345 (x,y) = c.split('-')
346 for i in range(int(x),int(y)+1):
347 cpus.append(int(i))
348 else:
349 # remove this element from the list
350 if c[0] == '^':
351 cpus = [x for x in cpus if x != int(c[1:])]
352 else:
353 cpus.append(int(c))
355 result['cpus'] = cpus
357 except ValueError, exn:
358 raise VmError(
359 'Invalid configuration setting: cpus = %s: %s' %
360 (result['cpus'], exn))
362 result['backend'] = []
363 for c in sxp.children(config, 'backend'):
364 result['backend'].append(sxp.name(sxp.child0(c)))
366 result['device'] = []
367 for d in sxp.children(config, 'device'):
368 c = sxp.child0(d)
369 result['device'].append((sxp.name(c), c))
371 # Configuration option "restart" is deprecated. Parse it, but
372 # let on_xyz override it if they are present.
373 restart = get_cfg('restart')
374 if restart:
375 def handle_restart(event, val):
376 if result[event] is None:
377 result[event] = val
379 if restart == "onreboot":
380 handle_restart('on_poweroff', 'destroy')
381 handle_restart('on_reboot', 'restart')
382 handle_restart('on_crash', 'destroy')
383 elif restart == "always":
384 handle_restart('on_poweroff', 'restart')
385 handle_restart('on_reboot', 'restart')
386 handle_restart('on_crash', 'restart')
387 elif restart == "never":
388 handle_restart('on_poweroff', 'destroy')
389 handle_restart('on_reboot', 'destroy')
390 handle_restart('on_crash', 'destroy')
391 else:
392 log.warn("Ignoring malformed and deprecated config option "
393 "restart = %s", restart)
395 log.debug("parseConfig: result is %s", result)
396 return result
399 def domain_by_name(name):
400 return XendDomain.instance().domain_lookup_by_name_nr(name)
403 def shutdown_reason(code):
404 """Get a shutdown reason from a code.
406 @param code: shutdown code
407 @type code: int
408 @return: shutdown reason
409 @rtype: string
410 """
411 return shutdown_reasons.get(code, "?")
413 def dom_get(dom):
414 """Get info from xen for an existing domain.
416 @param dom: domain id
417 @return: info or None
418 """
419 try:
420 domlist = xc.domain_getinfo(dom, 1)
421 if domlist and dom == domlist[0]['dom']:
422 return domlist[0]
423 except Exception, err:
424 # ignore missing domain
425 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
426 return None
429 class XendDomainInfo:
431 def __init__(self, info, domid = None, dompath = None, augment = False,
432 priv = False, resume = False):
434 self.info = info
436 if not self.infoIsSet('uuid'):
437 self.info['uuid'] = uuid.toString(uuid.create())
439 if domid is not None:
440 self.domid = domid
441 elif 'dom' in info:
442 self.domid = int(info['dom'])
443 else:
444 self.domid = None
446 self.vmpath = XendDomain.VMROOT + self.info['uuid']
447 self.dompath = dompath
449 if augment:
450 self.augmentInfo(priv)
452 self.validateInfo()
454 self.image = None
455 self.security = None
456 self.store_port = None
457 self.store_mfn = None
458 self.console_port = None
459 self.console_mfn = None
461 self.vmWatch = None
462 self.shutdownWatch = None
464 self.shutdownStartTime = None
466 self.state = STATE_DOM_OK
467 self.state_updated = threading.Condition()
468 self.refresh_shutdown_lock = threading.Condition()
470 self.setResume(resume)
472 ## private:
474 def readVMDetails(self, params):
475 """Read the specified parameters from the store.
476 """
477 try:
478 return self.gatherVm(*params)
479 except ValueError:
480 # One of the int/float entries in params has a corresponding store
481 # entry that is invalid. We recover, because older versions of
482 # Xend may have put the entry there (memory/target, for example),
483 # but this is in general a bad situation to have reached.
484 log.exception(
485 "Store corrupted at %s! Domain %d's configuration may be "
486 "affected.", self.vmpath, self.domid)
487 return []
490 def storeChanged(self, _):
491 log.trace("XendDomainInfo.storeChanged");
493 changed = False
495 def f(x, y):
496 if y is not None and self.info[x[0]] != y:
497 self.info[x[0]] = y
498 changed = True
500 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
502 im = self.readVm('image')
503 current_im = self.info['image']
504 if (im is not None and
505 (current_im is None or sxp.to_string(current_im) != im)):
506 self.info['image'] = sxp.from_string(im)
507 changed = True
509 if changed:
510 # Update the domain section of the store, as this contains some
511 # parameters derived from the VM configuration.
512 self.storeDomDetails()
514 return 1
517 def augmentInfo(self, priv):
518 """Augment self.info, as given to us through {@link #recreate}, with
519 values taken from the store. This recovers those values known to xend
520 but not to the hypervisor.
521 """
522 def useIfNeeded(name, val):
523 if not self.infoIsSet(name) and val is not None:
524 self.info[name] = val
526 if priv:
527 entries = VM_STORE_ENTRIES[:]
528 entries.remove(('memory', int))
529 entries.remove(('maxmem', int))
530 else:
531 entries = VM_STORE_ENTRIES
532 entries.append(('image', str))
533 entries.append(('security', str))
535 map(lambda x, y: useIfNeeded(x[0], y), entries,
536 self.readVMDetails(entries))
538 device = []
539 for c in controllerClasses:
540 devconfig = self.getDeviceConfigurations(c)
541 if devconfig:
542 device.extend(map(lambda x: (c, x), devconfig))
543 useIfNeeded('device', device)
546 def validateInfo(self):
547 """Validate and normalise the info block. This has either been parsed
548 by parseConfig, or received from xc through recreate and augmented by
549 the current store contents.
550 """
551 def defaultInfo(name, val):
552 if not self.infoIsSet(name):
553 self.info[name] = val()
555 try:
556 defaultInfo('name', lambda: "Domain-%d" % self.domid)
557 defaultInfo('on_poweroff', lambda: "destroy")
558 defaultInfo('on_reboot', lambda: "restart")
559 defaultInfo('on_crash', lambda: "restart")
560 defaultInfo('features', lambda: "")
561 defaultInfo('cpu', lambda: None)
562 defaultInfo('cpus', lambda: [])
563 defaultInfo('cpu_weight', lambda: 1.0)
565 # some domains don't have a config file (e.g. dom0 )
566 # to set number of vcpus so we derive available cpus
567 # from max_vcpu_id which is present for running domains.
568 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
569 avail = int(self.info['max_vcpu_id'])+1
570 else:
571 avail = int(1)
573 defaultInfo('vcpus', lambda: avail)
574 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
575 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
576 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
578 defaultInfo('memory', lambda: 0)
579 defaultInfo('shadow_memory', lambda: 0)
580 defaultInfo('maxmem', lambda: 0)
581 defaultInfo('bootloader', lambda: None)
582 defaultInfo('bootloader_args', lambda: None)
583 defaultInfo('backend', lambda: [])
584 defaultInfo('device', lambda: [])
585 defaultInfo('image', lambda: None)
586 defaultInfo('security', lambda: None)
588 self.check_name(self.info['name'])
590 if isinstance(self.info['image'], str):
591 self.info['image'] = sxp.from_string(self.info['image'])
593 if isinstance(self.info['security'], str):
594 self.info['security'] = sxp.from_string(self.info['security'])
596 if self.info['memory'] == 0:
597 if self.infoIsSet('mem_kb'):
598 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
599 if self.info['memory'] <= 0:
600 raise VmError('Invalid memory size')
602 if self.info['maxmem'] < self.info['memory']:
603 self.info['maxmem'] = self.info['memory']
605 for (n, c) in self.info['device']:
606 if not n or not c or n not in controllerClasses:
607 raise VmError('invalid device (%s, %s)' %
608 (str(n), str(c)))
610 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
611 if self.info[event] not in restart_modes:
612 raise VmError('invalid restart event: %s = %s' %
613 (event, str(self.info[event])))
615 except KeyError, exn:
616 log.exception(exn)
617 raise VmError('Unspecified domain detail: %s' % exn)
620 def readVm(self, *args):
621 return xstransact.Read(self.vmpath, *args)
623 def writeVm(self, *args):
624 return xstransact.Write(self.vmpath, *args)
626 def removeVm(self, *args):
627 return xstransact.Remove(self.vmpath, *args)
629 def gatherVm(self, *args):
630 return xstransact.Gather(self.vmpath, *args)
633 ## public:
635 def storeVm(self, *args):
636 return xstransact.Store(self.vmpath, *args)
639 ## private:
641 def readDom(self, *args):
642 return xstransact.Read(self.dompath, *args)
644 def writeDom(self, *args):
645 return xstransact.Write(self.dompath, *args)
648 ## public:
650 def removeDom(self, *args):
651 return xstransact.Remove(self.dompath, *args)
653 def recreateDom(self):
654 complete(self.dompath, lambda t: self._recreateDom(t))
656 def _recreateDom(self, t):
657 t.remove()
658 t.mkdir()
659 t.set_permissions({ 'dom' : self.domid })
662 ## private:
664 def storeDom(self, *args):
665 return xstransact.Store(self.dompath, *args)
668 ## public:
670 def completeRestore(self, store_mfn, console_mfn):
672 log.debug("XendDomainInfo.completeRestore")
674 self.store_mfn = store_mfn
675 self.console_mfn = console_mfn
677 self.introduceDomain()
678 self.storeDomDetails()
679 self.registerWatches()
680 self.refreshShutdown()
682 log.debug("XendDomainInfo.completeRestore done")
685 def storeVmDetails(self):
686 to_store = {}
688 for k in VM_STORE_ENTRIES:
689 if self.infoIsSet(k[0]):
690 to_store[k[0]] = str(self.info[k[0]])
692 if self.infoIsSet('image'):
693 to_store['image'] = sxp.to_string(self.info['image'])
695 if self.infoIsSet('security'):
696 security = self.info['security']
697 to_store['security'] = sxp.to_string(security)
698 for idx in range(0, len(security)):
699 if security[idx][0] == 'access_control':
700 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
701 for aidx in range(1, len(security[idx])):
702 if security[idx][aidx][0] == 'label':
703 to_store['security/access_control/label'] = security[idx][aidx][1]
704 if security[idx][aidx][0] == 'policy':
705 to_store['security/access_control/policy'] = security[idx][aidx][1]
706 if security[idx][0] == 'ssidref':
707 to_store['security/ssidref'] = str(security[idx][1])
709 if not self.readVm('xend/restart_count'):
710 to_store['xend/restart_count'] = str(0)
712 log.debug("Storing VM details: %s", to_store)
714 self.writeVm(to_store)
715 self.setVmPermissions()
718 def setVmPermissions(self):
719 """Allow the guest domain to read its UUID. We don't allow it to
720 access any other entry, for security."""
721 xstransact.SetPermissions('%s/uuid' % self.vmpath,
722 { 'dom' : self.domid,
723 'read' : True,
724 'write' : False })
727 def storeDomDetails(self):
728 to_store = {
729 'domid': str(self.domid),
730 'vm': self.vmpath,
731 'name': self.info['name'],
732 'console/limit': str(xroot.get_console_limit() * 1024),
733 'memory/target': str(self.info['memory'] * 1024)
734 }
736 def f(n, v):
737 if v is not None:
738 to_store[n] = str(v)
740 f('console/port', self.console_port)
741 f('console/ring-ref', self.console_mfn)
742 f('store/port', self.store_port)
743 f('store/ring-ref', self.store_mfn)
745 to_store.update(self.vcpuDomDetails())
747 log.debug("Storing domain details: %s", to_store)
749 self.writeDom(to_store)
752 ## private:
754 def vcpuDomDetails(self):
755 def availability(n):
756 if self.info['vcpu_avail'] & (1 << n):
757 return 'online'
758 else:
759 return 'offline'
761 result = {}
762 for v in range(0, self.info['vcpus']):
763 result["cpu/%d/availability" % v] = availability(v)
764 return result
767 ## public:
769 def registerWatches(self):
770 """Register a watch on this VM's entries in the store, and the
771 domain's control/shutdown node, so that when they are changed
772 externally, we keep up to date. This should only be called by {@link
773 #create}, {@link #recreate}, or {@link #restore}, once the domain's
774 details have been written, but before the new instance is returned."""
775 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
776 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
777 self.handleShutdownWatch)
780 def getDomid(self):
781 return self.domid
783 def setName(self, name):
784 self.check_name(name)
785 self.info['name'] = name
786 self.storeVm("name", name)
788 def getName(self):
789 return self.info['name']
791 def getDomainPath(self):
792 return self.dompath
795 def getStorePort(self):
796 """For use only by image.py and XendCheckpoint.py."""
797 return self.store_port
800 def getConsolePort(self):
801 """For use only by image.py and XendCheckpoint.py"""
802 return self.console_port
804 def getFeatures(self):
805 """For use only by image.py."""
806 return self.info['features']
808 def getVCpuCount(self):
809 return self.info['vcpus']
812 def setVCpuCount(self, vcpus):
813 self.info['vcpu_avail'] = (1 << vcpus) - 1
814 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
815 self.writeDom(self.vcpuDomDetails())
817 def getLabel(self):
818 return security.get_security_info(self.info, 'label')
820 def getMemoryTarget(self):
821 """Get this domain's target memory size, in KB."""
822 return self.info['memory'] * 1024
824 def getResume(self):
825 return "%s" % self.info['resume']
827 def endRestore(self):
828 self.setResume(False)
830 def setResume(self, state):
831 self.info['resume'] = state
833 def getRestartCount(self):
834 return self.readVm('xend/restart_count')
836 def refreshShutdown(self, xeninfo = None):
837 # If set at the end of this method, a restart is required, with the
838 # given reason. This restart has to be done out of the scope of
839 # refresh_shutdown_lock.
840 restart_reason = None
842 self.refresh_shutdown_lock.acquire()
843 try:
844 if xeninfo is None:
845 xeninfo = dom_get(self.domid)
846 if xeninfo is None:
847 # The domain no longer exists. This will occur if we have
848 # scheduled a timer to check for shutdown timeouts and the
849 # shutdown succeeded. It will also occur if someone
850 # destroys a domain beneath us. We clean up the domain,
851 # just in case, but we can't clean up the VM, because that
852 # VM may have migrated to a different domain on this
853 # machine.
854 self.cleanupDomain()
855 return
857 if xeninfo['dying']:
858 # Dying means that a domain has been destroyed, but has not
859 # yet been cleaned up by Xen. This state could persist
860 # indefinitely if, for example, another domain has some of its
861 # pages mapped. We might like to diagnose this problem in the
862 # future, but for now all we do is make sure that it's not us
863 # holding the pages, by calling cleanupDomain. We can't
864 # clean up the VM, as above.
865 self.cleanupDomain()
866 return
868 elif xeninfo['crashed']:
869 if self.readDom('xend/shutdown_completed'):
870 # We've seen this shutdown already, but we are preserving
871 # the domain for debugging. Leave it alone.
872 return
874 log.warn('Domain has crashed: name=%s id=%d.',
875 self.info['name'], self.domid)
877 if xroot.get_enable_dump():
878 self.dumpCore()
880 restart_reason = 'crash'
882 elif xeninfo['shutdown']:
883 if self.readDom('xend/shutdown_completed'):
884 # We've seen this shutdown already, but we are preserving
885 # the domain for debugging. Leave it alone.
886 return
888 else:
889 reason = shutdown_reason(xeninfo['shutdown_reason'])
891 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
892 self.info['name'], self.domid, reason)
894 self.clearRestart()
896 if reason == 'suspend':
897 self.state_set(STATE_DOM_SHUTDOWN)
898 # Don't destroy the domain. XendCheckpoint will do
899 # this once it has finished. However, stop watching
900 # the VM path now, otherwise we will end up with one
901 # watch for the old domain, and one for the new.
902 self.unwatchVm()
903 elif reason in ['poweroff', 'reboot']:
904 restart_reason = reason
905 else:
906 self.destroy()
908 elif self.dompath is None:
909 # We have yet to manage to call introduceDomain on this
910 # domain. This can happen if a restore is in progress, or has
911 # failed. Ignore this domain.
912 pass
913 else:
914 # Domain is alive. If we are shutting it down, then check
915 # the timeout on that, and destroy it if necessary.
917 if self.shutdownStartTime:
918 timeout = (SHUTDOWN_TIMEOUT - time.time() +
919 self.shutdownStartTime)
920 if timeout < 0:
921 log.info(
922 "Domain shutdown timeout expired: name=%s id=%s",
923 self.info['name'], self.domid)
924 self.destroy()
925 finally:
926 self.refresh_shutdown_lock.release()
928 if restart_reason:
929 self.maybeRestart(restart_reason)
932 def handleShutdownWatch(self, _):
933 log.debug('XendDomainInfo.handleShutdownWatch')
935 reason = self.readDom('control/shutdown')
937 if reason and reason != 'suspend':
938 sst = self.readDom('xend/shutdown_start_time')
939 now = time.time()
940 if sst:
941 self.shutdownStartTime = float(sst)
942 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
943 else:
944 self.shutdownStartTime = now
945 self.storeDom('xend/shutdown_start_time', now)
946 timeout = SHUTDOWN_TIMEOUT
948 log.trace(
949 "Scheduling refreshShutdown on domain %d in %ds.",
950 self.domid, timeout)
951 threading.Timer(timeout, self.refreshShutdown).start()
953 return True
956 def shutdown(self, reason):
957 if not reason in shutdown_reasons.values():
958 raise XendError('Invalid reason: %s' % reason)
959 if self.domid == 0:
960 raise XendError("Can't specify Domain-0")
961 self.storeDom("control/shutdown", reason)
964 ## private:
966 def clearRestart(self):
967 self.removeDom("xend/shutdown_start_time")
970 def maybeRestart(self, reason):
971 # Dispatch to the correct method based upon the configured on_{reason}
972 # behaviour.
973 {"destroy" : self.destroy,
974 "restart" : self.restart,
975 "preserve" : self.preserve,
976 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
979 def renameRestart(self):
980 self.restart(True)
983 def dumpCore(self,corefile=None):
984 """Create a core dump for this domain. Nothrow guarantee."""
986 try:
987 if not corefile:
988 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
989 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
990 self.info['name'], self.domid)
991 xc.domain_dumpcore(self.domid, corefile)
993 except:
994 corefile_incomp = corefile+'-incomplete'
995 os.rename(corefile, corefile_incomp)
996 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
997 self.domid, self.info['name'])
1000 ## public:
1002 def setMemoryTarget(self, target):
1003 """Set the memory target of this domain.
1004 @param target In MiB.
1005 """
1006 if target <= 0:
1007 raise XendError('Invalid memory size')
1009 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
1010 self.info['name'], self.domid, target)
1012 self.info['memory'] = target
1013 self.storeVm("memory", target)
1014 self.storeDom("memory/target", target << 10)
1017 def update(self, info = None):
1018 """Update with info from xc.domain_getinfo().
1019 """
1021 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1022 if not info:
1023 info = dom_get(self.domid)
1024 if not info:
1025 return
1027 #manually update ssidref / security fields
1028 if security.on() and info.has_key('ssidref'):
1029 if (info['ssidref'] != 0) and self.info.has_key('security'):
1030 security_field = self.info['security']
1031 if not security_field:
1032 #create new security element
1033 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1034 #ssidref field not used any longer
1035 info.pop('ssidref')
1037 self.info.update(info)
1038 self.validateInfo()
1039 self.refreshShutdown(info)
1041 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1042 self.info)
1045 ## private:
1047 def state_set(self, state):
1048 self.state_updated.acquire()
1049 try:
1050 if self.state != state:
1051 self.state = state
1052 self.state_updated.notifyAll()
1053 finally:
1054 self.state_updated.release()
1057 ## public:
1059 def waitForShutdown(self):
1060 self.state_updated.acquire()
1061 try:
1062 while self.state == STATE_DOM_OK:
1063 self.state_updated.wait()
1064 finally:
1065 self.state_updated.release()
1068 def __str__(self):
1069 s = "<domain"
1070 s += " id=" + str(self.domid)
1071 s += " name=" + self.info['name']
1072 s += " memory=" + str(self.info['memory'])
1073 s += ">"
1074 return s
1076 __repr__ = __str__
1079 ## private:
1081 def createDevice(self, deviceClass, devconfig):
1082 return self.getDeviceController(deviceClass).createDevice(devconfig)
1085 def waitForDevices_(self, deviceClass):
1086 return self.getDeviceController(deviceClass).waitForDevices()
1089 def waitForDevice(self, deviceClass, devid):
1090 return self.getDeviceController(deviceClass).waitForDevice(devid)
1093 def reconfigureDevice(self, deviceClass, devid, devconfig):
1094 return self.getDeviceController(deviceClass).reconfigureDevice(
1095 devid, devconfig)
1098 ## public:
1100 def destroyDevice(self, deviceClass, devid):
1101 if type(devid) is str:
1102 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
1103 for entry in xstransact.List(devicePath):
1104 backend = xstransact.Read('%s/%s' % (devicePath, entry), "backend")
1105 devName = xstransact.Read(backend, "dev")
1106 if devName == devid:
1107 # We found the integer matching our devid, use it instead
1108 devid = entry
1109 break
1110 return self.getDeviceController(deviceClass).destroyDevice(devid)
1113 def getDeviceSxprs(self, deviceClass):
1114 return self.getDeviceController(deviceClass).sxprs()
1117 ## private:
1119 def getDeviceConfigurations(self, deviceClass):
1120 return self.getDeviceController(deviceClass).configurations()
1123 def getDeviceController(self, name):
1124 if name not in controllerClasses:
1125 raise XendError("unknown device type: " + str(name))
1127 return controllerClasses[name](self)
1130 ## public:
1132 def sxpr(self):
1133 sxpr = ['domain',
1134 ['domid', self.domid]]
1136 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1137 if self.infoIsSet(e[0]):
1138 sxpr.append([e[0], self.info[e[0]]])
1140 if self.infoIsSet('image'):
1141 sxpr.append(['image', self.info['image']])
1143 if self.infoIsSet('security'):
1144 sxpr.append(['security', self.info['security']])
1146 for cls in controllerClasses:
1147 for config in self.getDeviceConfigurations(cls):
1148 sxpr.append(['device', config])
1150 def stateChar(name):
1151 if name in self.info:
1152 if self.info[name]:
1153 return name[0]
1154 else:
1155 return '-'
1156 else:
1157 return '?'
1159 state = reduce(
1160 lambda x, y: x + y,
1161 map(stateChar,
1162 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1163 'dying']))
1165 sxpr.append(['state', state])
1166 if self.infoIsSet('shutdown'):
1167 reason = shutdown_reason(self.info['shutdown_reason'])
1168 sxpr.append(['shutdown_reason', reason])
1169 if self.infoIsSet('cpu_time'):
1170 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1171 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1173 if self.infoIsSet('start_time'):
1174 up_time = time.time() - self.info['start_time']
1175 sxpr.append(['up_time', str(up_time) ])
1176 sxpr.append(['start_time', str(self.info['start_time']) ])
1178 if self.store_mfn:
1179 sxpr.append(['store_mfn', self.store_mfn])
1180 if self.console_mfn:
1181 sxpr.append(['console_mfn', self.console_mfn])
1183 return sxpr
1186 def getVCPUInfo(self):
1187 try:
1188 # We include the domain name and ID, to help xm.
1189 sxpr = ['domain',
1190 ['domid', self.domid],
1191 ['name', self.info['name']],
1192 ['vcpu_count', self.info['online_vcpus']]]
1194 for i in range(0, self.info['max_vcpu_id']+1):
1195 info = xc.vcpu_getinfo(self.domid, i)
1197 sxpr.append(['vcpu',
1198 ['number', i],
1199 ['online', info['online']],
1200 ['blocked', info['blocked']],
1201 ['running', info['running']],
1202 ['cpu_time', info['cpu_time'] / 1e9],
1203 ['cpu', info['cpu']],
1204 ['cpumap', info['cpumap']]])
1206 return sxpr
1208 except RuntimeError, exn:
1209 raise XendError(str(exn))
1212 ## private:
1214 def check_name(self, name):
1215 """Check if a vm name is valid. Valid names contain alphabetic characters,
1216 digits, or characters in '_-.:/+'.
1217 The same name cannot be used for more than one vm at the same time.
1219 @param name: name
1220 @raise: VmError if invalid
1221 """
1222 if name is None or name == '':
1223 raise VmError('missing vm name')
1224 for c in name:
1225 if c in string.digits: continue
1226 if c in '_-.:/+': continue
1227 if c in string.ascii_letters: continue
1228 raise VmError('invalid vm name')
1230 dominfo = domain_by_name(name)
1231 if not dominfo:
1232 return
1233 if self.domid is None:
1234 raise VmError("VM name '%s' already in use by domain %d" %
1235 (name, dominfo.domid))
1236 if dominfo.domid != self.domid:
1237 raise VmError("VM name '%s' is used in both domains %d and %d" %
1238 (name, self.domid, dominfo.domid))
1241 def construct(self):
1242 """Construct the domain.
1244 @raise: VmError on error
1245 """
1247 log.debug('XendDomainInfo.construct: %s',
1248 self.domid)
1250 self.domid = xc.domain_create(
1251 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1252 handle = uuid.fromString(self.info['uuid']))
1254 if self.domid < 0:
1255 raise VmError('Creating domain failed: name=%s' %
1256 self.info['name'])
1258 self.dompath = GetDomainPath(self.domid)
1260 self.recreateDom()
1262 # Set maximum number of vcpus in domain
1263 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1266 def introduceDomain(self):
1267 assert self.domid is not None
1268 assert self.store_mfn is not None
1269 assert self.store_port is not None
1271 try:
1272 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1273 except RuntimeError, exn:
1274 raise XendError(str(exn))
1277 def initDomain(self):
1278 log.debug('XendDomainInfo.initDomain: %s %s',
1279 self.domid,
1280 self.info['cpu_weight'])
1282 # if we have a boot loader but no image, then we need to set things
1283 # up by running the boot loader non-interactively
1284 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1285 self.configure_bootloader()
1287 if not self.infoIsSet('image'):
1288 raise VmError('Missing image in configuration')
1290 try:
1291 self.image = image.create(self,
1292 self.info['image'],
1293 self.info['device'])
1295 localtime = self.info['localtime']
1296 if localtime is not None and localtime == 1:
1297 xc.domain_set_time_offset(self.domid)
1299 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1301 # repin domain vcpus if a restricted cpus list is provided
1302 # this is done prior to memory allocation to aide in memory
1303 # distribution for NUMA systems.
1304 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1305 for v in range(0, self.info['max_vcpu_id']+1):
1306 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1308 # Use architecture- and image-specific calculations to determine
1309 # the various headrooms necessary, given the raw configured
1310 # values.
1311 # reservation, maxmem, memory, and shadow are all in KiB.
1312 reservation = self.image.getRequiredInitialReservation(
1313 self.info['memory'] * 1024)
1314 maxmem = self.image.getRequiredAvailableMemory(
1315 self.info['maxmem'] * 1024)
1316 memory = self.image.getRequiredAvailableMemory(
1317 self.info['memory'] * 1024)
1318 shadow = self.image.getRequiredShadowMemory(
1319 self.info['shadow_memory'] * 1024,
1320 self.info['maxmem'] * 1024)
1322 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1323 # takes MiB and we must not round down and end up under-providing.
1324 shadow = ((shadow + 1023) / 1024) * 1024
1326 # set memory limit
1327 xc.domain_setmaxmem(self.domid, maxmem)
1329 # Make sure there's enough RAM available for the domain
1330 balloon.free(memory + shadow)
1332 # Set up the shadow memory
1333 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1334 self.info['shadow_memory'] = shadow_cur
1336 # initial memory reservation
1337 xc.domain_memory_increase_reservation(self.domid, reservation, 0,
1338 0)
1340 self.createChannels()
1342 channel_details = self.image.createImage()
1344 self.store_mfn = channel_details['store_mfn']
1345 if 'console_mfn' in channel_details:
1346 self.console_mfn = channel_details['console_mfn']
1348 self.introduceDomain()
1350 self.createDevices()
1352 if self.info['bootloader']:
1353 self.image.cleanupBootloading()
1355 self.info['start_time'] = time.time()
1357 except RuntimeError, exn:
1358 raise VmError(str(exn))
1361 ## public:
1363 def cleanupDomain(self):
1364 """Cleanup domain resources; release devices. Idempotent. Nothrow
1365 guarantee."""
1367 self.refresh_shutdown_lock.acquire()
1368 try:
1369 self.unwatchShutdown()
1371 self.release_devices()
1373 if self.image:
1374 try:
1375 self.image.destroy()
1376 except:
1377 log.exception(
1378 "XendDomainInfo.cleanup: image.destroy() failed.")
1379 self.image = None
1381 try:
1382 self.removeDom()
1383 except:
1384 log.exception("Removing domain path failed.")
1386 try:
1387 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1388 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1389 except:
1390 log.exception("Renaming Zombie failed.")
1392 self.state_set(STATE_DOM_SHUTDOWN)
1393 finally:
1394 self.refresh_shutdown_lock.release()
1397 def cleanupVm(self):
1398 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1400 self.unwatchVm()
1402 try:
1403 self.removeVm()
1404 except:
1405 log.exception("Removing VM path failed.")
1408 ## private:
1410 def unwatchVm(self):
1411 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1412 guarantee."""
1414 try:
1415 try:
1416 if self.vmWatch:
1417 self.vmWatch.unwatch()
1418 finally:
1419 self.vmWatch = None
1420 except:
1421 log.exception("Unwatching VM path failed.")
1424 def unwatchShutdown(self):
1425 """Remove the watch on the domain's control/shutdown node, if any.
1426 Idempotent. Nothrow guarantee. Expects to be protected by the
1427 refresh_shutdown_lock."""
1429 try:
1430 try:
1431 if self.shutdownWatch:
1432 self.shutdownWatch.unwatch()
1433 finally:
1434 self.shutdownWatch = None
1435 except:
1436 log.exception("Unwatching control/shutdown failed.")
1439 ## public:
1441 def destroy(self):
1442 """Cleanup VM and destroy domain. Nothrow guarantee."""
1444 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1446 self.cleanupVm()
1447 if self.dompath is not None:
1448 self.destroyDomain()
1451 def destroyDomain(self):
1452 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1454 try:
1455 if self.domid is not None:
1456 xc.domain_destroy(self.domid)
1457 except:
1458 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1460 self.cleanupDomain()
1463 ## private:
1465 def release_devices(self):
1466 """Release all domain's devices. Nothrow guarantee."""
1468 while True:
1469 t = xstransact("%s/device" % self.dompath)
1470 for n in controllerClasses.keys():
1471 for d in t.list(n):
1472 try:
1473 t.remove(d)
1474 except:
1475 # Log and swallow any exceptions in removal --
1476 # there's nothing more we can do.
1477 log.exception(
1478 "Device release failed: %s; %s; %s",
1479 self.info['name'], n, d)
1480 if t.commit():
1481 break
1484 def createChannels(self):
1485 """Create the channels to the domain.
1486 """
1487 self.store_port = self.createChannel()
1488 self.console_port = self.createChannel()
1491 def createChannel(self):
1492 """Create an event channel to the domain.
1493 """
1494 try:
1495 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1496 except:
1497 log.exception("Exception in alloc_unbound(%d)", self.domid)
1498 raise
1501 ## public:
1503 def createDevices(self):
1504 """Create the devices for a vm.
1506 @raise: VmError for invalid devices
1507 """
1509 for (n, c) in self.info['device']:
1510 self.createDevice(n, c)
1512 if self.image:
1513 self.image.createDeviceModel()
1515 ## public:
1517 def checkLiveMigrateMemory(self):
1518 """ Make sure there's enough memory to migrate this domain """
1519 overhead_kb = 0
1520 if arch.type == "x86":
1521 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1522 # the minimum that Xen would allocate if no value were given.
1523 overhead_kb = self.info['vcpus'] * 1024 + self.info['maxmem'] * 4
1524 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1525 # The domain might already have some shadow memory
1526 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1527 if overhead_kb > 0:
1528 balloon.free(overhead_kb)
1530 def testMigrateDevices(self, network, dst):
1531 """ Notify all device about intention of migration
1532 @raise: XendError for a device that cannot be migrated
1533 """
1534 for (n, c) in self.info['device']:
1535 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1536 if rc != 0:
1537 raise XendError("Device of type '%s' refuses migration." % n)
1539 def testDeviceComplete(self):
1540 """ For Block IO migration safety we must ensure that
1541 the device has shutdown correctly, i.e. all blocks are
1542 flushed to disk
1543 """
1544 start = time.time()
1545 while True:
1546 test = 0
1547 diff = time.time() - start
1548 for i in self.getDeviceController('vbd').deviceIDs():
1549 test = 1
1550 log.info("Dev %s still active, looping...", i)
1551 time.sleep(0.1)
1553 if test == 0:
1554 break
1555 if diff >= MIGRATE_TIMEOUT:
1556 log.info("Dev still active but hit max loop timeout")
1557 break
1559 def migrateDevices(self, network, dst, step, domName=''):
1560 """Notify the devices about migration
1561 """
1562 ctr = 0
1563 try:
1564 for (n, c) in self.info['device']:
1565 self.migrateDevice(n, c, network, dst, step, domName)
1566 ctr = ctr + 1
1567 except:
1568 for (n, c) in self.info['device']:
1569 if ctr == 0:
1570 step = step - 1
1571 ctr = ctr - 1
1572 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1573 raise
1575 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1576 step, domName=''):
1577 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1578 network, dst, step, domName)
1580 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1581 dst, step, domName=''):
1582 return self.getDeviceController(deviceClass).recover_migrate(
1583 deviceConfig, network, dst, step, domName)
1585 def waitForDevices(self):
1586 """Wait for this domain's configured devices to connect.
1588 @raise: VmError if any device fails to initialise.
1589 """
1590 for c in controllerClasses:
1591 self.waitForDevices_(c)
1594 def device_create(self, dev_config):
1595 """Create a new device.
1597 @param dev_config: device configuration
1598 """
1599 dev_type = sxp.name(dev_config)
1600 devid = self.createDevice(dev_type, dev_config)
1601 self.waitForDevice(dev_type, devid)
1602 self.info['device'].append((dev_type, dev_config))
1603 return self.getDeviceController(dev_type).sxpr(devid)
1606 def device_configure(self, dev_config):
1607 """Configure an existing device.
1608 @param dev_config: device configuration
1609 """
1610 deviceClass = sxp.name(dev_config)
1611 self.reconfigureDevice(deviceClass, None, dev_config)
1614 def pause(self):
1615 xc.domain_pause(self.domid)
1618 def unpause(self):
1619 xc.domain_unpause(self.domid)
1622 ## private:
1624 def restart(self, rename = False):
1625 """Restart the domain after it has exited.
1627 @param rename True if the old domain is to be renamed and preserved,
1628 False if it is to be destroyed.
1629 """
1631 self.configure_bootloader()
1632 config = self.sxpr()
1634 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1635 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1636 self.info['cpus'])])
1638 if self.readVm(RESTART_IN_PROGRESS):
1639 log.error('Xend failed during restart of domain %d. '
1640 'Refusing to restart to avoid loops.',
1641 self.domid)
1642 self.destroy()
1643 return
1645 self.writeVm(RESTART_IN_PROGRESS, 'True')
1647 now = time.time()
1648 rst = self.readVm('xend/previous_restart_time')
1649 if rst:
1650 rst = float(rst)
1651 timeout = now - rst
1652 if timeout < MINIMUM_RESTART_TIME:
1653 log.error(
1654 'VM %s restarting too fast (%f seconds since the last '
1655 'restart). Refusing to restart to avoid loops.',
1656 self.info['name'], timeout)
1657 self.destroy()
1658 return
1660 self.writeVm('xend/previous_restart_time', str(now))
1662 try:
1663 if rename:
1664 self.preserveForRestart()
1665 else:
1666 self.unwatchVm()
1667 self.destroyDomain()
1669 # new_dom's VM will be the same as this domain's VM, except where
1670 # the rename flag has instructed us to call preserveForRestart.
1671 # In that case, it is important that we remove the
1672 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1673 # once the new one is available.
1675 new_dom = None
1676 try:
1677 new_dom = XendDomain.instance().domain_create(config)
1678 new_dom.unpause()
1679 rst_cnt = self.readVm('xend/restart_count')
1680 rst_cnt = int(rst_cnt) + 1
1681 self.writeVm('xend/restart_count', str(rst_cnt))
1682 new_dom.removeVm(RESTART_IN_PROGRESS)
1683 except:
1684 if new_dom:
1685 new_dom.removeVm(RESTART_IN_PROGRESS)
1686 new_dom.destroy()
1687 else:
1688 self.removeVm(RESTART_IN_PROGRESS)
1689 raise
1690 except:
1691 log.exception('Failed to restart domain %d.', self.domid)
1694 def preserveForRestart(self):
1695 """Preserve a domain that has been shut down, by giving it a new UUID,
1696 cloning the VM details, and giving it a new name. This allows us to
1697 keep this domain for debugging, but restart a new one in its place
1698 preserving the restart semantics (name and UUID preserved).
1699 """
1701 new_name = self.generateUniqueName()
1702 new_uuid = uuid.toString(uuid.create())
1703 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1704 self.info['name'], self.domid, self.info['uuid'],
1705 new_name, new_uuid)
1706 self.unwatchVm()
1707 self.release_devices()
1708 self.info['name'] = new_name
1709 self.info['uuid'] = new_uuid
1710 self.vmpath = XendDomain.VMROOT + new_uuid
1711 self.storeVmDetails()
1712 self.preserve()
1715 def preserve(self):
1716 log.info("Preserving dead domain %s (%d).", self.info['name'],
1717 self.domid)
1718 self.unwatchVm()
1719 self.storeDom('xend/shutdown_completed', 'True')
1720 self.state_set(STATE_DOM_SHUTDOWN)
1723 # private:
1725 def generateUniqueName(self):
1726 n = 1
1727 while True:
1728 name = "%s-%d" % (self.info['name'], n)
1729 try:
1730 self.check_name(name)
1731 return name
1732 except VmError:
1733 n += 1
1736 def configure_bootloader(self):
1737 """Run the bootloader if we're configured to do so."""
1738 if not self.info['bootloader']:
1739 return
1740 blcfg = None
1741 # FIXME: this assumes that we want to use the first disk device
1742 for (n,c) in self.info['device']:
1743 if not n or not c or n != "vbd":
1744 continue
1745 disk = sxp.child_value(c, "uname")
1746 if disk is None:
1747 continue
1748 fn = blkdev_uname_to_file(disk)
1749 blcfg = bootloader(self.info['bootloader'], fn, 1,
1750 self.info['bootloader_args'],
1751 self.info['image'])
1752 break
1753 if blcfg is None:
1754 msg = "Had a bootloader specified, but can't find disk"
1755 log.error(msg)
1756 raise VmError(msg)
1757 self.info['image'] = blcfg
1760 def send_sysrq(self, key):
1761 asserts.isCharConvertible(key)
1763 self.storeDom("control/sysrq", '%c' % key)
1766 def infoIsSet(self, name):
1767 return name in self.info and self.info[name] is not None
1770 #============================================================================
1771 # Register device controllers and their device config types.
1773 """A map from device-class names to the subclass of DevController that
1774 implements the device control specific to that device-class."""
1775 controllerClasses = {}
1777 def addControllerClass(device_class, cls):
1778 """Register a subclass of DevController to handle the named device-class.
1779 """
1780 cls.deviceClass = device_class
1781 controllerClasses[device_class] = cls
1784 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1785 from xen.xend.server.BlktapController import BlktapController
1786 addControllerClass('vbd', blkif.BlkifController)
1787 addControllerClass('vif', netif.NetifController)
1788 addControllerClass('vtpm', tpmif.TPMifController)
1789 addControllerClass('pci', pciif.PciController)
1790 addControllerClass('ioports', iopif.IOPortsController)
1791 addControllerClass('irq', irqif.IRQController)
1792 addControllerClass('usb', usbif.UsbifController)
1793 addControllerClass('tap', BlktapController)