ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 11586:332bdaab35fa

[XEND] Remove hard tabs from destroyDevice()
Reported by Hollis Blanchard.

Signed-off-by: Alastair Tse <atse@xensource.com>
author atse@norwich.uk.xensource.com
date Fri Sep 22 15:56:19 2006 +0100 (2006-09-22)
parents 1db4e40b4d83
children e6388ec26a97
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),
1105 "backend")
1106 devName = xstransact.Read(backend, "dev")
1107 if devName == devid:
1108 # We found the integer matching our devid, use it instead
1109 devid = entry
1110 break
1111 return self.getDeviceController(deviceClass).destroyDevice(devid)
1114 def getDeviceSxprs(self, deviceClass):
1115 return self.getDeviceController(deviceClass).sxprs()
1118 ## private:
1120 def getDeviceConfigurations(self, deviceClass):
1121 return self.getDeviceController(deviceClass).configurations()
1124 def getDeviceController(self, name):
1125 if name not in controllerClasses:
1126 raise XendError("unknown device type: " + str(name))
1128 return controllerClasses[name](self)
1131 ## public:
1133 def sxpr(self):
1134 sxpr = ['domain',
1135 ['domid', self.domid]]
1137 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1138 if self.infoIsSet(e[0]):
1139 sxpr.append([e[0], self.info[e[0]]])
1141 if self.infoIsSet('image'):
1142 sxpr.append(['image', self.info['image']])
1144 if self.infoIsSet('security'):
1145 sxpr.append(['security', self.info['security']])
1147 for cls in controllerClasses:
1148 for config in self.getDeviceConfigurations(cls):
1149 sxpr.append(['device', config])
1151 def stateChar(name):
1152 if name in self.info:
1153 if self.info[name]:
1154 return name[0]
1155 else:
1156 return '-'
1157 else:
1158 return '?'
1160 state = reduce(
1161 lambda x, y: x + y,
1162 map(stateChar,
1163 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1164 'dying']))
1166 sxpr.append(['state', state])
1167 if self.infoIsSet('shutdown'):
1168 reason = shutdown_reason(self.info['shutdown_reason'])
1169 sxpr.append(['shutdown_reason', reason])
1170 if self.infoIsSet('cpu_time'):
1171 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1172 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1174 if self.infoIsSet('start_time'):
1175 up_time = time.time() - self.info['start_time']
1176 sxpr.append(['up_time', str(up_time) ])
1177 sxpr.append(['start_time', str(self.info['start_time']) ])
1179 if self.store_mfn:
1180 sxpr.append(['store_mfn', self.store_mfn])
1181 if self.console_mfn:
1182 sxpr.append(['console_mfn', self.console_mfn])
1184 return sxpr
1187 def getVCPUInfo(self):
1188 try:
1189 # We include the domain name and ID, to help xm.
1190 sxpr = ['domain',
1191 ['domid', self.domid],
1192 ['name', self.info['name']],
1193 ['vcpu_count', self.info['online_vcpus']]]
1195 for i in range(0, self.info['max_vcpu_id']+1):
1196 info = xc.vcpu_getinfo(self.domid, i)
1198 sxpr.append(['vcpu',
1199 ['number', i],
1200 ['online', info['online']],
1201 ['blocked', info['blocked']],
1202 ['running', info['running']],
1203 ['cpu_time', info['cpu_time'] / 1e9],
1204 ['cpu', info['cpu']],
1205 ['cpumap', info['cpumap']]])
1207 return sxpr
1209 except RuntimeError, exn:
1210 raise XendError(str(exn))
1213 ## private:
1215 def check_name(self, name):
1216 """Check if a vm name is valid. Valid names contain alphabetic characters,
1217 digits, or characters in '_-.:/+'.
1218 The same name cannot be used for more than one vm at the same time.
1220 @param name: name
1221 @raise: VmError if invalid
1222 """
1223 if name is None or name == '':
1224 raise VmError('missing vm name')
1225 for c in name:
1226 if c in string.digits: continue
1227 if c in '_-.:/+': continue
1228 if c in string.ascii_letters: continue
1229 raise VmError('invalid vm name')
1231 dominfo = domain_by_name(name)
1232 if not dominfo:
1233 return
1234 if self.domid is None:
1235 raise VmError("VM name '%s' already in use by domain %d" %
1236 (name, dominfo.domid))
1237 if dominfo.domid != self.domid:
1238 raise VmError("VM name '%s' is used in both domains %d and %d" %
1239 (name, self.domid, dominfo.domid))
1242 def construct(self):
1243 """Construct the domain.
1245 @raise: VmError on error
1246 """
1248 log.debug('XendDomainInfo.construct: %s',
1249 self.domid)
1251 self.domid = xc.domain_create(
1252 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1253 handle = uuid.fromString(self.info['uuid']))
1255 if self.domid < 0:
1256 raise VmError('Creating domain failed: name=%s' %
1257 self.info['name'])
1259 self.dompath = GetDomainPath(self.domid)
1261 self.recreateDom()
1263 # Set maximum number of vcpus in domain
1264 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1267 def introduceDomain(self):
1268 assert self.domid is not None
1269 assert self.store_mfn is not None
1270 assert self.store_port is not None
1272 try:
1273 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1274 except RuntimeError, exn:
1275 raise XendError(str(exn))
1278 def initDomain(self):
1279 log.debug('XendDomainInfo.initDomain: %s %s',
1280 self.domid,
1281 self.info['cpu_weight'])
1283 # if we have a boot loader but no image, then we need to set things
1284 # up by running the boot loader non-interactively
1285 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1286 self.configure_bootloader()
1288 if not self.infoIsSet('image'):
1289 raise VmError('Missing image in configuration')
1291 try:
1292 self.image = image.create(self,
1293 self.info['image'],
1294 self.info['device'])
1296 localtime = self.info['localtime']
1297 if localtime is not None and localtime == 1:
1298 xc.domain_set_time_offset(self.domid)
1300 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1302 # repin domain vcpus if a restricted cpus list is provided
1303 # this is done prior to memory allocation to aide in memory
1304 # distribution for NUMA systems.
1305 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1306 for v in range(0, self.info['max_vcpu_id']+1):
1307 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1309 # Use architecture- and image-specific calculations to determine
1310 # the various headrooms necessary, given the raw configured
1311 # values.
1312 # reservation, maxmem, memory, and shadow are all in KiB.
1313 reservation = self.image.getRequiredInitialReservation(
1314 self.info['memory'] * 1024)
1315 maxmem = self.image.getRequiredAvailableMemory(
1316 self.info['maxmem'] * 1024)
1317 memory = self.image.getRequiredAvailableMemory(
1318 self.info['memory'] * 1024)
1319 shadow = self.image.getRequiredShadowMemory(
1320 self.info['shadow_memory'] * 1024,
1321 self.info['maxmem'] * 1024)
1323 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1324 # takes MiB and we must not round down and end up under-providing.
1325 shadow = ((shadow + 1023) / 1024) * 1024
1327 # set memory limit
1328 xc.domain_setmaxmem(self.domid, maxmem)
1330 # Make sure there's enough RAM available for the domain
1331 balloon.free(memory + shadow)
1333 # Set up the shadow memory
1334 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1335 self.info['shadow_memory'] = shadow_cur
1337 # initial memory reservation
1338 xc.domain_memory_increase_reservation(self.domid, reservation, 0,
1339 0)
1341 self.createChannels()
1343 channel_details = self.image.createImage()
1345 self.store_mfn = channel_details['store_mfn']
1346 if 'console_mfn' in channel_details:
1347 self.console_mfn = channel_details['console_mfn']
1349 self.introduceDomain()
1351 self.createDevices()
1353 if self.info['bootloader']:
1354 self.image.cleanupBootloading()
1356 self.info['start_time'] = time.time()
1358 except RuntimeError, exn:
1359 raise VmError(str(exn))
1362 ## public:
1364 def cleanupDomain(self):
1365 """Cleanup domain resources; release devices. Idempotent. Nothrow
1366 guarantee."""
1368 self.refresh_shutdown_lock.acquire()
1369 try:
1370 self.unwatchShutdown()
1372 self.release_devices()
1374 if self.image:
1375 try:
1376 self.image.destroy()
1377 except:
1378 log.exception(
1379 "XendDomainInfo.cleanup: image.destroy() failed.")
1380 self.image = None
1382 try:
1383 self.removeDom()
1384 except:
1385 log.exception("Removing domain path failed.")
1387 try:
1388 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1389 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1390 except:
1391 log.exception("Renaming Zombie failed.")
1393 self.state_set(STATE_DOM_SHUTDOWN)
1394 finally:
1395 self.refresh_shutdown_lock.release()
1398 def cleanupVm(self):
1399 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1401 self.unwatchVm()
1403 try:
1404 self.removeVm()
1405 except:
1406 log.exception("Removing VM path failed.")
1409 ## private:
1411 def unwatchVm(self):
1412 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1413 guarantee."""
1415 try:
1416 try:
1417 if self.vmWatch:
1418 self.vmWatch.unwatch()
1419 finally:
1420 self.vmWatch = None
1421 except:
1422 log.exception("Unwatching VM path failed.")
1425 def unwatchShutdown(self):
1426 """Remove the watch on the domain's control/shutdown node, if any.
1427 Idempotent. Nothrow guarantee. Expects to be protected by the
1428 refresh_shutdown_lock."""
1430 try:
1431 try:
1432 if self.shutdownWatch:
1433 self.shutdownWatch.unwatch()
1434 finally:
1435 self.shutdownWatch = None
1436 except:
1437 log.exception("Unwatching control/shutdown failed.")
1440 ## public:
1442 def destroy(self):
1443 """Cleanup VM and destroy domain. Nothrow guarantee."""
1445 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1447 self.cleanupVm()
1448 if self.dompath is not None:
1449 self.destroyDomain()
1452 def destroyDomain(self):
1453 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1455 try:
1456 if self.domid is not None:
1457 xc.domain_destroy(self.domid)
1458 except:
1459 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1461 self.cleanupDomain()
1464 ## private:
1466 def release_devices(self):
1467 """Release all domain's devices. Nothrow guarantee."""
1469 while True:
1470 t = xstransact("%s/device" % self.dompath)
1471 for n in controllerClasses.keys():
1472 for d in t.list(n):
1473 try:
1474 t.remove(d)
1475 except:
1476 # Log and swallow any exceptions in removal --
1477 # there's nothing more we can do.
1478 log.exception(
1479 "Device release failed: %s; %s; %s",
1480 self.info['name'], n, d)
1481 if t.commit():
1482 break
1485 def createChannels(self):
1486 """Create the channels to the domain.
1487 """
1488 self.store_port = self.createChannel()
1489 self.console_port = self.createChannel()
1492 def createChannel(self):
1493 """Create an event channel to the domain.
1494 """
1495 try:
1496 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1497 except:
1498 log.exception("Exception in alloc_unbound(%d)", self.domid)
1499 raise
1502 ## public:
1504 def createDevices(self):
1505 """Create the devices for a vm.
1507 @raise: VmError for invalid devices
1508 """
1510 for (n, c) in self.info['device']:
1511 self.createDevice(n, c)
1513 if self.image:
1514 self.image.createDeviceModel()
1516 ## public:
1518 def checkLiveMigrateMemory(self):
1519 """ Make sure there's enough memory to migrate this domain """
1520 overhead_kb = 0
1521 if arch.type == "x86":
1522 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1523 # the minimum that Xen would allocate if no value were given.
1524 overhead_kb = self.info['vcpus'] * 1024 + self.info['maxmem'] * 4
1525 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1526 # The domain might already have some shadow memory
1527 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1528 if overhead_kb > 0:
1529 balloon.free(overhead_kb)
1531 def testMigrateDevices(self, network, dst):
1532 """ Notify all device about intention of migration
1533 @raise: XendError for a device that cannot be migrated
1534 """
1535 for (n, c) in self.info['device']:
1536 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1537 if rc != 0:
1538 raise XendError("Device of type '%s' refuses migration." % n)
1540 def testDeviceComplete(self):
1541 """ For Block IO migration safety we must ensure that
1542 the device has shutdown correctly, i.e. all blocks are
1543 flushed to disk
1544 """
1545 start = time.time()
1546 while True:
1547 test = 0
1548 diff = time.time() - start
1549 for i in self.getDeviceController('vbd').deviceIDs():
1550 test = 1
1551 log.info("Dev %s still active, looping...", i)
1552 time.sleep(0.1)
1554 if test == 0:
1555 break
1556 if diff >= MIGRATE_TIMEOUT:
1557 log.info("Dev still active but hit max loop timeout")
1558 break
1560 def migrateDevices(self, network, dst, step, domName=''):
1561 """Notify the devices about migration
1562 """
1563 ctr = 0
1564 try:
1565 for (n, c) in self.info['device']:
1566 self.migrateDevice(n, c, network, dst, step, domName)
1567 ctr = ctr + 1
1568 except:
1569 for (n, c) in self.info['device']:
1570 if ctr == 0:
1571 step = step - 1
1572 ctr = ctr - 1
1573 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1574 raise
1576 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1577 step, domName=''):
1578 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1579 network, dst, step, domName)
1581 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1582 dst, step, domName=''):
1583 return self.getDeviceController(deviceClass).recover_migrate(
1584 deviceConfig, network, dst, step, domName)
1586 def waitForDevices(self):
1587 """Wait for this domain's configured devices to connect.
1589 @raise: VmError if any device fails to initialise.
1590 """
1591 for c in controllerClasses:
1592 self.waitForDevices_(c)
1595 def device_create(self, dev_config):
1596 """Create a new device.
1598 @param dev_config: device configuration
1599 """
1600 dev_type = sxp.name(dev_config)
1601 devid = self.createDevice(dev_type, dev_config)
1602 self.waitForDevice(dev_type, devid)
1603 self.info['device'].append((dev_type, dev_config))
1604 return self.getDeviceController(dev_type).sxpr(devid)
1607 def device_configure(self, dev_config):
1608 """Configure an existing device.
1609 @param dev_config: device configuration
1610 """
1611 deviceClass = sxp.name(dev_config)
1612 self.reconfigureDevice(deviceClass, None, dev_config)
1615 def pause(self):
1616 xc.domain_pause(self.domid)
1619 def unpause(self):
1620 xc.domain_unpause(self.domid)
1623 ## private:
1625 def restart(self, rename = False):
1626 """Restart the domain after it has exited.
1628 @param rename True if the old domain is to be renamed and preserved,
1629 False if it is to be destroyed.
1630 """
1632 self.configure_bootloader()
1633 config = self.sxpr()
1635 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1636 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1637 self.info['cpus'])])
1639 if self.readVm(RESTART_IN_PROGRESS):
1640 log.error('Xend failed during restart of domain %d. '
1641 'Refusing to restart to avoid loops.',
1642 self.domid)
1643 self.destroy()
1644 return
1646 self.writeVm(RESTART_IN_PROGRESS, 'True')
1648 now = time.time()
1649 rst = self.readVm('xend/previous_restart_time')
1650 if rst:
1651 rst = float(rst)
1652 timeout = now - rst
1653 if timeout < MINIMUM_RESTART_TIME:
1654 log.error(
1655 'VM %s restarting too fast (%f seconds since the last '
1656 'restart). Refusing to restart to avoid loops.',
1657 self.info['name'], timeout)
1658 self.destroy()
1659 return
1661 self.writeVm('xend/previous_restart_time', str(now))
1663 try:
1664 if rename:
1665 self.preserveForRestart()
1666 else:
1667 self.unwatchVm()
1668 self.destroyDomain()
1670 # new_dom's VM will be the same as this domain's VM, except where
1671 # the rename flag has instructed us to call preserveForRestart.
1672 # In that case, it is important that we remove the
1673 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1674 # once the new one is available.
1676 new_dom = None
1677 try:
1678 new_dom = XendDomain.instance().domain_create(config)
1679 new_dom.unpause()
1680 rst_cnt = self.readVm('xend/restart_count')
1681 rst_cnt = int(rst_cnt) + 1
1682 self.writeVm('xend/restart_count', str(rst_cnt))
1683 new_dom.removeVm(RESTART_IN_PROGRESS)
1684 except:
1685 if new_dom:
1686 new_dom.removeVm(RESTART_IN_PROGRESS)
1687 new_dom.destroy()
1688 else:
1689 self.removeVm(RESTART_IN_PROGRESS)
1690 raise
1691 except:
1692 log.exception('Failed to restart domain %d.', self.domid)
1695 def preserveForRestart(self):
1696 """Preserve a domain that has been shut down, by giving it a new UUID,
1697 cloning the VM details, and giving it a new name. This allows us to
1698 keep this domain for debugging, but restart a new one in its place
1699 preserving the restart semantics (name and UUID preserved).
1700 """
1702 new_name = self.generateUniqueName()
1703 new_uuid = uuid.toString(uuid.create())
1704 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1705 self.info['name'], self.domid, self.info['uuid'],
1706 new_name, new_uuid)
1707 self.unwatchVm()
1708 self.release_devices()
1709 self.info['name'] = new_name
1710 self.info['uuid'] = new_uuid
1711 self.vmpath = XendDomain.VMROOT + new_uuid
1712 self.storeVmDetails()
1713 self.preserve()
1716 def preserve(self):
1717 log.info("Preserving dead domain %s (%d).", self.info['name'],
1718 self.domid)
1719 self.unwatchVm()
1720 self.storeDom('xend/shutdown_completed', 'True')
1721 self.state_set(STATE_DOM_SHUTDOWN)
1724 # private:
1726 def generateUniqueName(self):
1727 n = 1
1728 while True:
1729 name = "%s-%d" % (self.info['name'], n)
1730 try:
1731 self.check_name(name)
1732 return name
1733 except VmError:
1734 n += 1
1737 def configure_bootloader(self):
1738 """Run the bootloader if we're configured to do so."""
1739 if not self.info['bootloader']:
1740 return
1741 blcfg = None
1742 # FIXME: this assumes that we want to use the first disk device
1743 for (n,c) in self.info['device']:
1744 if not n or not c or n != "vbd":
1745 continue
1746 disk = sxp.child_value(c, "uname")
1747 if disk is None:
1748 continue
1749 fn = blkdev_uname_to_file(disk)
1750 blcfg = bootloader(self.info['bootloader'], fn, 1,
1751 self.info['bootloader_args'],
1752 self.info['image'])
1753 break
1754 if blcfg is None:
1755 msg = "Had a bootloader specified, but can't find disk"
1756 log.error(msg)
1757 raise VmError(msg)
1758 self.info['image'] = blcfg
1761 def send_sysrq(self, key):
1762 asserts.isCharConvertible(key)
1764 self.storeDom("control/sysrq", '%c' % key)
1767 def infoIsSet(self, name):
1768 return name in self.info and self.info[name] is not None
1771 #============================================================================
1772 # Register device controllers and their device config types.
1774 """A map from device-class names to the subclass of DevController that
1775 implements the device control specific to that device-class."""
1776 controllerClasses = {}
1778 def addControllerClass(device_class, cls):
1779 """Register a subclass of DevController to handle the named device-class.
1780 """
1781 cls.deviceClass = device_class
1782 controllerClasses[device_class] = cls
1785 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1786 from xen.xend.server.BlktapController import BlktapController
1787 addControllerClass('vbd', blkif.BlkifController)
1788 addControllerClass('vif', netif.NetifController)
1789 addControllerClass('vtpm', tpmif.TPMifController)
1790 addControllerClass('pci', pciif.PciController)
1791 addControllerClass('ioports', iopif.IOPortsController)
1792 addControllerClass('irq', irqif.IRQController)
1793 addControllerClass('usb', usbif.UsbifController)
1794 addControllerClass('tap', BlktapController)