direct-io.hg

view tools/python/xen/xend/XendDomainInfo.py @ 11583:e6388ec26a97

[XEND] Do not attempt core dump to directories.

- Abort core dump if destination is a directory.
- Throw appropriate XendError expection on failure.
Reported by Masaki Kanno <kanno.masaki@jp.fujitsu.com>.

Signed-off-by: Alastair Tse <atse@xensource.com>
author atse@norwich.uk.xensource.com
date Fri Sep 22 16:20:36 2006 +0100 (2006-09-22)
parents 332bdaab35fa
children bf0deb09facd 9a932b5c7947
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)
992 if os.path.isdir(corefile):
993 raise XendError("Cannot dump core in a directory: %s" %
994 corefile)
996 xc.domain_dumpcore(self.domid, corefile)
997 except RuntimeError, ex:
998 corefile_incomp = corefile+'-incomplete'
999 os.rename(corefile, corefile_incomp)
1000 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1001 self.domid, self.info['name'])
1002 raise XendError("Failed to dump core: %s" % str(ex))
1004 ## public:
1006 def setMemoryTarget(self, target):
1007 """Set the memory target of this domain.
1008 @param target In MiB.
1009 """
1010 if target <= 0:
1011 raise XendError('Invalid memory size')
1013 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
1014 self.info['name'], self.domid, target)
1016 self.info['memory'] = target
1017 self.storeVm("memory", target)
1018 self.storeDom("memory/target", target << 10)
1021 def update(self, info = None):
1022 """Update with info from xc.domain_getinfo().
1023 """
1025 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1026 if not info:
1027 info = dom_get(self.domid)
1028 if not info:
1029 return
1031 #manually update ssidref / security fields
1032 if security.on() and info.has_key('ssidref'):
1033 if (info['ssidref'] != 0) and self.info.has_key('security'):
1034 security_field = self.info['security']
1035 if not security_field:
1036 #create new security element
1037 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1038 #ssidref field not used any longer
1039 info.pop('ssidref')
1041 self.info.update(info)
1042 self.validateInfo()
1043 self.refreshShutdown(info)
1045 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1046 self.info)
1049 ## private:
1051 def state_set(self, state):
1052 self.state_updated.acquire()
1053 try:
1054 if self.state != state:
1055 self.state = state
1056 self.state_updated.notifyAll()
1057 finally:
1058 self.state_updated.release()
1061 ## public:
1063 def waitForShutdown(self):
1064 self.state_updated.acquire()
1065 try:
1066 while self.state == STATE_DOM_OK:
1067 self.state_updated.wait()
1068 finally:
1069 self.state_updated.release()
1072 def __str__(self):
1073 s = "<domain"
1074 s += " id=" + str(self.domid)
1075 s += " name=" + self.info['name']
1076 s += " memory=" + str(self.info['memory'])
1077 s += ">"
1078 return s
1080 __repr__ = __str__
1083 ## private:
1085 def createDevice(self, deviceClass, devconfig):
1086 return self.getDeviceController(deviceClass).createDevice(devconfig)
1089 def waitForDevices_(self, deviceClass):
1090 return self.getDeviceController(deviceClass).waitForDevices()
1093 def waitForDevice(self, deviceClass, devid):
1094 return self.getDeviceController(deviceClass).waitForDevice(devid)
1097 def reconfigureDevice(self, deviceClass, devid, devconfig):
1098 return self.getDeviceController(deviceClass).reconfigureDevice(
1099 devid, devconfig)
1102 ## public:
1104 def destroyDevice(self, deviceClass, devid):
1105 if type(devid) is str:
1106 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
1107 for entry in xstransact.List(devicePath):
1108 backend = xstransact.Read('%s/%s' % (devicePath, entry),
1109 "backend")
1110 devName = xstransact.Read(backend, "dev")
1111 if devName == devid:
1112 # We found the integer matching our devid, use it instead
1113 devid = entry
1114 break
1115 return self.getDeviceController(deviceClass).destroyDevice(devid)
1118 def getDeviceSxprs(self, deviceClass):
1119 return self.getDeviceController(deviceClass).sxprs()
1122 ## private:
1124 def getDeviceConfigurations(self, deviceClass):
1125 return self.getDeviceController(deviceClass).configurations()
1128 def getDeviceController(self, name):
1129 if name not in controllerClasses:
1130 raise XendError("unknown device type: " + str(name))
1132 return controllerClasses[name](self)
1135 ## public:
1137 def sxpr(self):
1138 sxpr = ['domain',
1139 ['domid', self.domid]]
1141 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1142 if self.infoIsSet(e[0]):
1143 sxpr.append([e[0], self.info[e[0]]])
1145 if self.infoIsSet('image'):
1146 sxpr.append(['image', self.info['image']])
1148 if self.infoIsSet('security'):
1149 sxpr.append(['security', self.info['security']])
1151 for cls in controllerClasses:
1152 for config in self.getDeviceConfigurations(cls):
1153 sxpr.append(['device', config])
1155 def stateChar(name):
1156 if name in self.info:
1157 if self.info[name]:
1158 return name[0]
1159 else:
1160 return '-'
1161 else:
1162 return '?'
1164 state = reduce(
1165 lambda x, y: x + y,
1166 map(stateChar,
1167 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1168 'dying']))
1170 sxpr.append(['state', state])
1171 if self.infoIsSet('shutdown'):
1172 reason = shutdown_reason(self.info['shutdown_reason'])
1173 sxpr.append(['shutdown_reason', reason])
1174 if self.infoIsSet('cpu_time'):
1175 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1176 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1178 if self.infoIsSet('start_time'):
1179 up_time = time.time() - self.info['start_time']
1180 sxpr.append(['up_time', str(up_time) ])
1181 sxpr.append(['start_time', str(self.info['start_time']) ])
1183 if self.store_mfn:
1184 sxpr.append(['store_mfn', self.store_mfn])
1185 if self.console_mfn:
1186 sxpr.append(['console_mfn', self.console_mfn])
1188 return sxpr
1191 def getVCPUInfo(self):
1192 try:
1193 # We include the domain name and ID, to help xm.
1194 sxpr = ['domain',
1195 ['domid', self.domid],
1196 ['name', self.info['name']],
1197 ['vcpu_count', self.info['online_vcpus']]]
1199 for i in range(0, self.info['max_vcpu_id']+1):
1200 info = xc.vcpu_getinfo(self.domid, i)
1202 sxpr.append(['vcpu',
1203 ['number', i],
1204 ['online', info['online']],
1205 ['blocked', info['blocked']],
1206 ['running', info['running']],
1207 ['cpu_time', info['cpu_time'] / 1e9],
1208 ['cpu', info['cpu']],
1209 ['cpumap', info['cpumap']]])
1211 return sxpr
1213 except RuntimeError, exn:
1214 raise XendError(str(exn))
1217 ## private:
1219 def check_name(self, name):
1220 """Check if a vm name is valid. Valid names contain alphabetic characters,
1221 digits, or characters in '_-.:/+'.
1222 The same name cannot be used for more than one vm at the same time.
1224 @param name: name
1225 @raise: VmError if invalid
1226 """
1227 if name is None or name == '':
1228 raise VmError('missing vm name')
1229 for c in name:
1230 if c in string.digits: continue
1231 if c in '_-.:/+': continue
1232 if c in string.ascii_letters: continue
1233 raise VmError('invalid vm name')
1235 dominfo = domain_by_name(name)
1236 if not dominfo:
1237 return
1238 if self.domid is None:
1239 raise VmError("VM name '%s' already in use by domain %d" %
1240 (name, dominfo.domid))
1241 if dominfo.domid != self.domid:
1242 raise VmError("VM name '%s' is used in both domains %d and %d" %
1243 (name, self.domid, dominfo.domid))
1246 def construct(self):
1247 """Construct the domain.
1249 @raise: VmError on error
1250 """
1252 log.debug('XendDomainInfo.construct: %s',
1253 self.domid)
1255 self.domid = xc.domain_create(
1256 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1257 handle = uuid.fromString(self.info['uuid']))
1259 if self.domid < 0:
1260 raise VmError('Creating domain failed: name=%s' %
1261 self.info['name'])
1263 self.dompath = GetDomainPath(self.domid)
1265 self.recreateDom()
1267 # Set maximum number of vcpus in domain
1268 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1271 def introduceDomain(self):
1272 assert self.domid is not None
1273 assert self.store_mfn is not None
1274 assert self.store_port is not None
1276 try:
1277 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1278 except RuntimeError, exn:
1279 raise XendError(str(exn))
1282 def initDomain(self):
1283 log.debug('XendDomainInfo.initDomain: %s %s',
1284 self.domid,
1285 self.info['cpu_weight'])
1287 # if we have a boot loader but no image, then we need to set things
1288 # up by running the boot loader non-interactively
1289 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1290 self.configure_bootloader()
1292 if not self.infoIsSet('image'):
1293 raise VmError('Missing image in configuration')
1295 try:
1296 self.image = image.create(self,
1297 self.info['image'],
1298 self.info['device'])
1300 localtime = self.info['localtime']
1301 if localtime is not None and localtime == 1:
1302 xc.domain_set_time_offset(self.domid)
1304 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1306 # repin domain vcpus if a restricted cpus list is provided
1307 # this is done prior to memory allocation to aide in memory
1308 # distribution for NUMA systems.
1309 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1310 for v in range(0, self.info['max_vcpu_id']+1):
1311 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1313 # Use architecture- and image-specific calculations to determine
1314 # the various headrooms necessary, given the raw configured
1315 # values.
1316 # reservation, maxmem, memory, and shadow are all in KiB.
1317 reservation = self.image.getRequiredInitialReservation(
1318 self.info['memory'] * 1024)
1319 maxmem = self.image.getRequiredAvailableMemory(
1320 self.info['maxmem'] * 1024)
1321 memory = self.image.getRequiredAvailableMemory(
1322 self.info['memory'] * 1024)
1323 shadow = self.image.getRequiredShadowMemory(
1324 self.info['shadow_memory'] * 1024,
1325 self.info['maxmem'] * 1024)
1327 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1328 # takes MiB and we must not round down and end up under-providing.
1329 shadow = ((shadow + 1023) / 1024) * 1024
1331 # set memory limit
1332 xc.domain_setmaxmem(self.domid, maxmem)
1334 # Make sure there's enough RAM available for the domain
1335 balloon.free(memory + shadow)
1337 # Set up the shadow memory
1338 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1339 self.info['shadow_memory'] = shadow_cur
1341 # initial memory reservation
1342 xc.domain_memory_increase_reservation(self.domid, reservation, 0,
1343 0)
1345 self.createChannels()
1347 channel_details = self.image.createImage()
1349 self.store_mfn = channel_details['store_mfn']
1350 if 'console_mfn' in channel_details:
1351 self.console_mfn = channel_details['console_mfn']
1353 self.introduceDomain()
1355 self.createDevices()
1357 if self.info['bootloader']:
1358 self.image.cleanupBootloading()
1360 self.info['start_time'] = time.time()
1362 except RuntimeError, exn:
1363 raise VmError(str(exn))
1366 ## public:
1368 def cleanupDomain(self):
1369 """Cleanup domain resources; release devices. Idempotent. Nothrow
1370 guarantee."""
1372 self.refresh_shutdown_lock.acquire()
1373 try:
1374 self.unwatchShutdown()
1376 self.release_devices()
1378 if self.image:
1379 try:
1380 self.image.destroy()
1381 except:
1382 log.exception(
1383 "XendDomainInfo.cleanup: image.destroy() failed.")
1384 self.image = None
1386 try:
1387 self.removeDom()
1388 except:
1389 log.exception("Removing domain path failed.")
1391 try:
1392 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1393 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1394 except:
1395 log.exception("Renaming Zombie failed.")
1397 self.state_set(STATE_DOM_SHUTDOWN)
1398 finally:
1399 self.refresh_shutdown_lock.release()
1402 def cleanupVm(self):
1403 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1405 self.unwatchVm()
1407 try:
1408 self.removeVm()
1409 except:
1410 log.exception("Removing VM path failed.")
1413 ## private:
1415 def unwatchVm(self):
1416 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1417 guarantee."""
1419 try:
1420 try:
1421 if self.vmWatch:
1422 self.vmWatch.unwatch()
1423 finally:
1424 self.vmWatch = None
1425 except:
1426 log.exception("Unwatching VM path failed.")
1429 def unwatchShutdown(self):
1430 """Remove the watch on the domain's control/shutdown node, if any.
1431 Idempotent. Nothrow guarantee. Expects to be protected by the
1432 refresh_shutdown_lock."""
1434 try:
1435 try:
1436 if self.shutdownWatch:
1437 self.shutdownWatch.unwatch()
1438 finally:
1439 self.shutdownWatch = None
1440 except:
1441 log.exception("Unwatching control/shutdown failed.")
1444 ## public:
1446 def destroy(self):
1447 """Cleanup VM and destroy domain. Nothrow guarantee."""
1449 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1451 self.cleanupVm()
1452 if self.dompath is not None:
1453 self.destroyDomain()
1456 def destroyDomain(self):
1457 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1459 try:
1460 if self.domid is not None:
1461 xc.domain_destroy(self.domid)
1462 except:
1463 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1465 self.cleanupDomain()
1468 ## private:
1470 def release_devices(self):
1471 """Release all domain's devices. Nothrow guarantee."""
1473 while True:
1474 t = xstransact("%s/device" % self.dompath)
1475 for n in controllerClasses.keys():
1476 for d in t.list(n):
1477 try:
1478 t.remove(d)
1479 except:
1480 # Log and swallow any exceptions in removal --
1481 # there's nothing more we can do.
1482 log.exception(
1483 "Device release failed: %s; %s; %s",
1484 self.info['name'], n, d)
1485 if t.commit():
1486 break
1489 def createChannels(self):
1490 """Create the channels to the domain.
1491 """
1492 self.store_port = self.createChannel()
1493 self.console_port = self.createChannel()
1496 def createChannel(self):
1497 """Create an event channel to the domain.
1498 """
1499 try:
1500 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1501 except:
1502 log.exception("Exception in alloc_unbound(%d)", self.domid)
1503 raise
1506 ## public:
1508 def createDevices(self):
1509 """Create the devices for a vm.
1511 @raise: VmError for invalid devices
1512 """
1514 for (n, c) in self.info['device']:
1515 self.createDevice(n, c)
1517 if self.image:
1518 self.image.createDeviceModel()
1520 ## public:
1522 def checkLiveMigrateMemory(self):
1523 """ Make sure there's enough memory to migrate this domain """
1524 overhead_kb = 0
1525 if arch.type == "x86":
1526 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1527 # the minimum that Xen would allocate if no value were given.
1528 overhead_kb = self.info['vcpus'] * 1024 + self.info['maxmem'] * 4
1529 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1530 # The domain might already have some shadow memory
1531 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1532 if overhead_kb > 0:
1533 balloon.free(overhead_kb)
1535 def testMigrateDevices(self, network, dst):
1536 """ Notify all device about intention of migration
1537 @raise: XendError for a device that cannot be migrated
1538 """
1539 for (n, c) in self.info['device']:
1540 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1541 if rc != 0:
1542 raise XendError("Device of type '%s' refuses migration." % n)
1544 def testDeviceComplete(self):
1545 """ For Block IO migration safety we must ensure that
1546 the device has shutdown correctly, i.e. all blocks are
1547 flushed to disk
1548 """
1549 start = time.time()
1550 while True:
1551 test = 0
1552 diff = time.time() - start
1553 for i in self.getDeviceController('vbd').deviceIDs():
1554 test = 1
1555 log.info("Dev %s still active, looping...", i)
1556 time.sleep(0.1)
1558 if test == 0:
1559 break
1560 if diff >= MIGRATE_TIMEOUT:
1561 log.info("Dev still active but hit max loop timeout")
1562 break
1564 def migrateDevices(self, network, dst, step, domName=''):
1565 """Notify the devices about migration
1566 """
1567 ctr = 0
1568 try:
1569 for (n, c) in self.info['device']:
1570 self.migrateDevice(n, c, network, dst, step, domName)
1571 ctr = ctr + 1
1572 except:
1573 for (n, c) in self.info['device']:
1574 if ctr == 0:
1575 step = step - 1
1576 ctr = ctr - 1
1577 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1578 raise
1580 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1581 step, domName=''):
1582 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1583 network, dst, step, domName)
1585 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1586 dst, step, domName=''):
1587 return self.getDeviceController(deviceClass).recover_migrate(
1588 deviceConfig, network, dst, step, domName)
1590 def waitForDevices(self):
1591 """Wait for this domain's configured devices to connect.
1593 @raise: VmError if any device fails to initialise.
1594 """
1595 for c in controllerClasses:
1596 self.waitForDevices_(c)
1599 def device_create(self, dev_config):
1600 """Create a new device.
1602 @param dev_config: device configuration
1603 """
1604 dev_type = sxp.name(dev_config)
1605 devid = self.createDevice(dev_type, dev_config)
1606 self.waitForDevice(dev_type, devid)
1607 self.info['device'].append((dev_type, dev_config))
1608 return self.getDeviceController(dev_type).sxpr(devid)
1611 def device_configure(self, dev_config):
1612 """Configure an existing device.
1613 @param dev_config: device configuration
1614 """
1615 deviceClass = sxp.name(dev_config)
1616 self.reconfigureDevice(deviceClass, None, dev_config)
1619 def pause(self):
1620 xc.domain_pause(self.domid)
1623 def unpause(self):
1624 xc.domain_unpause(self.domid)
1627 ## private:
1629 def restart(self, rename = False):
1630 """Restart the domain after it has exited.
1632 @param rename True if the old domain is to be renamed and preserved,
1633 False if it is to be destroyed.
1634 """
1636 self.configure_bootloader()
1637 config = self.sxpr()
1639 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1640 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1641 self.info['cpus'])])
1643 if self.readVm(RESTART_IN_PROGRESS):
1644 log.error('Xend failed during restart of domain %d. '
1645 'Refusing to restart to avoid loops.',
1646 self.domid)
1647 self.destroy()
1648 return
1650 self.writeVm(RESTART_IN_PROGRESS, 'True')
1652 now = time.time()
1653 rst = self.readVm('xend/previous_restart_time')
1654 if rst:
1655 rst = float(rst)
1656 timeout = now - rst
1657 if timeout < MINIMUM_RESTART_TIME:
1658 log.error(
1659 'VM %s restarting too fast (%f seconds since the last '
1660 'restart). Refusing to restart to avoid loops.',
1661 self.info['name'], timeout)
1662 self.destroy()
1663 return
1665 self.writeVm('xend/previous_restart_time', str(now))
1667 try:
1668 if rename:
1669 self.preserveForRestart()
1670 else:
1671 self.unwatchVm()
1672 self.destroyDomain()
1674 # new_dom's VM will be the same as this domain's VM, except where
1675 # the rename flag has instructed us to call preserveForRestart.
1676 # In that case, it is important that we remove the
1677 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1678 # once the new one is available.
1680 new_dom = None
1681 try:
1682 new_dom = XendDomain.instance().domain_create(config)
1683 new_dom.unpause()
1684 rst_cnt = self.readVm('xend/restart_count')
1685 rst_cnt = int(rst_cnt) + 1
1686 self.writeVm('xend/restart_count', str(rst_cnt))
1687 new_dom.removeVm(RESTART_IN_PROGRESS)
1688 except:
1689 if new_dom:
1690 new_dom.removeVm(RESTART_IN_PROGRESS)
1691 new_dom.destroy()
1692 else:
1693 self.removeVm(RESTART_IN_PROGRESS)
1694 raise
1695 except:
1696 log.exception('Failed to restart domain %d.', self.domid)
1699 def preserveForRestart(self):
1700 """Preserve a domain that has been shut down, by giving it a new UUID,
1701 cloning the VM details, and giving it a new name. This allows us to
1702 keep this domain for debugging, but restart a new one in its place
1703 preserving the restart semantics (name and UUID preserved).
1704 """
1706 new_name = self.generateUniqueName()
1707 new_uuid = uuid.toString(uuid.create())
1708 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1709 self.info['name'], self.domid, self.info['uuid'],
1710 new_name, new_uuid)
1711 self.unwatchVm()
1712 self.release_devices()
1713 self.info['name'] = new_name
1714 self.info['uuid'] = new_uuid
1715 self.vmpath = XendDomain.VMROOT + new_uuid
1716 self.storeVmDetails()
1717 self.preserve()
1720 def preserve(self):
1721 log.info("Preserving dead domain %s (%d).", self.info['name'],
1722 self.domid)
1723 self.unwatchVm()
1724 self.storeDom('xend/shutdown_completed', 'True')
1725 self.state_set(STATE_DOM_SHUTDOWN)
1728 # private:
1730 def generateUniqueName(self):
1731 n = 1
1732 while True:
1733 name = "%s-%d" % (self.info['name'], n)
1734 try:
1735 self.check_name(name)
1736 return name
1737 except VmError:
1738 n += 1
1741 def configure_bootloader(self):
1742 """Run the bootloader if we're configured to do so."""
1743 if not self.info['bootloader']:
1744 return
1745 blcfg = None
1746 # FIXME: this assumes that we want to use the first disk device
1747 for (n,c) in self.info['device']:
1748 if not n or not c or n != "vbd":
1749 continue
1750 disk = sxp.child_value(c, "uname")
1751 if disk is None:
1752 continue
1753 fn = blkdev_uname_to_file(disk)
1754 blcfg = bootloader(self.info['bootloader'], fn, 1,
1755 self.info['bootloader_args'],
1756 self.info['image'])
1757 break
1758 if blcfg is None:
1759 msg = "Had a bootloader specified, but can't find disk"
1760 log.error(msg)
1761 raise VmError(msg)
1762 self.info['image'] = blcfg
1765 def send_sysrq(self, key):
1766 asserts.isCharConvertible(key)
1768 self.storeDom("control/sysrq", '%c' % key)
1771 def infoIsSet(self, name):
1772 return name in self.info and self.info[name] is not None
1775 #============================================================================
1776 # Register device controllers and their device config types.
1778 """A map from device-class names to the subclass of DevController that
1779 implements the device control specific to that device-class."""
1780 controllerClasses = {}
1782 def addControllerClass(device_class, cls):
1783 """Register a subclass of DevController to handle the named device-class.
1784 """
1785 cls.deviceClass = device_class
1786 controllerClasses[device_class] = cls
1789 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1790 from xen.xend.server.BlktapController import BlktapController
1791 addControllerClass('vbd', blkif.BlkifController)
1792 addControllerClass('vif', netif.NetifController)
1793 addControllerClass('vtpm', tpmif.TPMifController)
1794 addControllerClass('pci', pciif.PciController)
1795 addControllerClass('ioports', iopif.IOPortsController)
1796 addControllerClass('irq', irqif.IRQController)
1797 addControllerClass('usb', usbif.UsbifController)
1798 addControllerClass('tap', BlktapController)