ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 11346:a47951e59cbf

Shadow memory should be in MiB, not KiB. It also needs rounding up to be safe.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Wed Aug 30 09:47:24 2006 +0100 (2006-08-30)
parents 24258e322e88
children dc773bf49664
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005, 2006 XenSource Ltd
17 #============================================================================
19 """Representation of a single domain.
20 Includes support for domain construction, using
21 open-ended configurations.
23 Author: Mike Wray <mike.wray@hp.com>
25 """
27 import errno
28 import logging
29 import string
30 import time
31 import threading
32 import os
34 import xen.lowlevel.xc
35 from xen.util import asserts
36 from xen.util.blkif import blkdev_uname_to_file
37 from xen.util import security
38 import balloon
39 import image
40 import sxp
41 import uuid
42 import XendDomain
43 import XendRoot
45 from xen.xend.XendBootloader import bootloader
46 from xen.xend.XendError import XendError, VmError
48 from xen.xend.xenstore.xstransact import xstransact, complete
49 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain
50 from xen.xend.xenstore.xswatch import xswatch
53 """Shutdown code for poweroff."""
54 DOMAIN_POWEROFF = 0
56 """Shutdown code for reboot."""
57 DOMAIN_REBOOT = 1
59 """Shutdown code for suspend."""
60 DOMAIN_SUSPEND = 2
62 """Shutdown code for crash."""
63 DOMAIN_CRASH = 3
65 """Shutdown code for halt."""
66 DOMAIN_HALT = 4
68 """Map shutdown codes to strings."""
69 shutdown_reasons = {
70 DOMAIN_POWEROFF: "poweroff",
71 DOMAIN_REBOOT : "reboot",
72 DOMAIN_SUSPEND : "suspend",
73 DOMAIN_CRASH : "crash",
74 DOMAIN_HALT : "halt"
75 }
77 restart_modes = [
78 "restart",
79 "destroy",
80 "preserve",
81 "rename-restart"
82 ]
84 STATE_DOM_OK = 1
85 STATE_DOM_SHUTDOWN = 2
87 SHUTDOWN_TIMEOUT = 30.0
89 ZOMBIE_PREFIX = 'Zombie-'
91 """Constants for the different stages of ext. device migration """
92 DEV_MIGRATE_TEST = 0
93 DEV_MIGRATE_STEP1 = 1
94 DEV_MIGRATE_STEP2 = 2
95 DEV_MIGRATE_STEP3 = 3
97 """Minimum time between domain restarts in seconds."""
98 MINIMUM_RESTART_TIME = 20
100 RESTART_IN_PROGRESS = 'xend/restart_in_progress'
103 xc = xen.lowlevel.xc.xc()
104 xroot = XendRoot.instance()
106 log = logging.getLogger("xend.XendDomainInfo")
107 #log.setLevel(logging.TRACE)
110 ##
111 # All parameters of VMs that may be configured on-the-fly, or at start-up.
112 #
113 VM_CONFIG_PARAMS = [
114 ('name', str),
115 ('on_poweroff', str),
116 ('on_reboot', str),
117 ('on_crash', str),
118 ]
121 ##
122 # Configuration entries that we expect to round-trip -- be read from the
123 # config file or xc, written to save-files (i.e. through sxpr), and reused as
124 # config on restart or restore, all without munging. Some configuration
125 # entries are munged for backwards compatibility reasons, or because they
126 # don't come out of xc in the same form as they are specified in the config
127 # file, so those are handled separately.
128 ROUNDTRIPPING_CONFIG_ENTRIES = [
129 ('uuid', str),
130 ('vcpus', int),
131 ('vcpu_avail', int),
132 ('cpu_weight', float),
133 ('memory', int),
134 ('shadow_memory', int),
135 ('maxmem', int),
136 ('bootloader', str),
137 ('bootloader_args', str),
138 ('features', str),
139 ('localtime', int),
140 ]
142 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
145 ##
146 # All entries written to the store. This is VM_CONFIG_PARAMS, plus those
147 # entries written to the store that cannot be reconfigured on-the-fly.
148 #
149 VM_STORE_ENTRIES = [
150 ('uuid', str),
151 ('vcpus', int),
152 ('vcpu_avail', int),
153 ('memory', int),
154 ('shadow_memory', int),
155 ('maxmem', int),
156 ('start_time', float),
157 ]
159 VM_STORE_ENTRIES += VM_CONFIG_PARAMS
162 #
163 # There are a number of CPU-related fields:
164 #
165 # vcpus: the number of virtual CPUs this domain is configured to use.
166 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
167 # its VCPUs. This is translated to
168 # <dompath>/cpu/<id>/availability = {online,offline} for use
169 # by the guest domain.
170 # cpumap: a list of bitmaps, one for each VCPU, giving the physical
171 # CPUs that that VCPU may use.
172 # cpu: a configuration setting requesting that VCPU 0 is pinned to
173 # the specified physical CPU.
174 #
175 # vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
176 # across save, restore, migrate, and restart). The other settings are only
177 # specific to the domain, so are lost when the VM moves.
178 #
181 def create(config):
182 """Create a VM from a configuration.
184 @param config configuration
185 @raise: VmError for invalid configuration
186 """
188 log.debug("XendDomainInfo.create(%s)", config)
190 vm = XendDomainInfo(parseConfig(config))
191 try:
192 vm.construct()
193 vm.initDomain()
194 vm.storeVmDetails()
195 vm.storeDomDetails()
196 vm.registerWatches()
197 vm.refreshShutdown()
198 return vm
199 except:
200 log.exception('Domain construction failed')
201 vm.destroy()
202 raise
205 def recreate(xeninfo, priv):
206 """Create the VM object for an existing domain. The domain must not
207 be dying, as the paths in the store should already have been removed,
208 and asking us to recreate them causes problems."""
210 log.debug("XendDomainInfo.recreate(%s)", xeninfo)
212 assert not xeninfo['dying']
214 domid = xeninfo['dom']
215 uuid1 = xeninfo['handle']
216 xeninfo['uuid'] = uuid.toString(uuid1)
217 dompath = GetDomainPath(domid)
218 if not dompath:
219 raise XendError(
220 'No domain path in store for existing domain %d' % domid)
222 log.info("Recreating domain %d, UUID %s.", domid, xeninfo['uuid'])
223 try:
224 vmpath = xstransact.Read(dompath, "vm")
225 if not vmpath:
226 raise XendError(
227 'No vm path in store for existing domain %d' % domid)
228 uuid2_str = xstransact.Read(vmpath, "uuid")
229 if not uuid2_str:
230 raise XendError(
231 'No vm/uuid path in store for existing domain %d' % domid)
233 uuid2 = uuid.fromString(uuid2_str)
235 if uuid1 != uuid2:
236 raise XendError(
237 'Uuid in store does not match uuid for existing domain %d: '
238 '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
240 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
242 except Exception, exn:
243 if priv:
244 log.warn(str(exn))
246 vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
247 vm.recreateDom()
248 vm.removeVm()
249 vm.storeVmDetails()
250 vm.storeDomDetails()
252 vm.registerWatches()
253 vm.refreshShutdown(xeninfo)
254 return vm
257 def restore(config):
258 """Create a domain and a VM object to do a restore.
260 @param config: domain configuration
261 """
263 log.debug("XendDomainInfo.restore(%s)", config)
265 vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
266 try:
267 vm.construct()
268 vm.storeVmDetails()
269 vm.createDevices()
270 vm.createChannels()
271 vm.storeDomDetails()
272 vm.endRestore()
273 return vm
274 except:
275 vm.destroy()
276 raise
279 def parseConfig(config):
280 def get_cfg(name, conv = None):
281 val = sxp.child_value(config, name)
283 if conv and not val is None:
284 try:
285 return conv(val)
286 except TypeError, exn:
287 raise VmError(
288 'Invalid setting %s = %s in configuration: %s' %
289 (name, val, str(exn)))
290 else:
291 return val
294 log.debug("parseConfig: config is %s", config)
296 result = {}
298 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
299 result[e[0]] = get_cfg(e[0], e[1])
301 result['cpu'] = get_cfg('cpu', int)
302 result['cpus'] = get_cfg('cpus', str)
303 result['image'] = get_cfg('image')
304 tmp_security = get_cfg('security')
305 if tmp_security:
306 result['security'] = tmp_security
308 try:
309 if result['image']:
310 v = sxp.child_value(result['image'], 'vcpus')
311 if result['vcpus'] is None and v is not None:
312 result['vcpus'] = int(v)
313 elif v is not None and int(v) != result['vcpus']:
314 log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
315 ' Using %s VCPUs for VM %s.') %
316 (result['vcpus'], v, result['uuid']))
317 result['vcpus'] = int(v)
318 except TypeError, exn:
319 raise VmError(
320 'Invalid configuration setting: vcpus = %s: %s' %
321 (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
323 try:
324 # support legacy config files with 'cpu' parameter
325 # NB: prepending to list to support previous behavior
326 # where 'cpu' parameter pinned VCPU0.
327 if result['cpu']:
328 if result['cpus']:
329 result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
330 else:
331 result['cpus'] = str(result['cpu'])
333 # convert 'cpus' string to list of ints
334 # 'cpus' supports a list of ranges (0-3), seperated by
335 # commas, and negation, (^1).
336 # Precedence is settled by order of the string:
337 # "0-3,^1" -> [0,2,3]
338 # "0-3,^1,1" -> [0,1,2,3]
339 if result['cpus']:
340 cpus = []
341 for c in result['cpus'].split(','):
342 if c.find('-') != -1:
343 (x,y) = c.split('-')
344 for i in range(int(x),int(y)+1):
345 cpus.append(int(i))
346 else:
347 # remove this element from the list
348 if c[0] == '^':
349 cpus = [x for x in cpus if x != int(c[1:])]
350 else:
351 cpus.append(int(c))
353 result['cpus'] = cpus
355 except ValueError, exn:
356 raise VmError(
357 'Invalid configuration setting: cpus = %s: %s' %
358 (result['cpus'], exn))
360 result['backend'] = []
361 for c in sxp.children(config, 'backend'):
362 result['backend'].append(sxp.name(sxp.child0(c)))
364 result['device'] = []
365 for d in sxp.children(config, 'device'):
366 c = sxp.child0(d)
367 result['device'].append((sxp.name(c), c))
369 # Configuration option "restart" is deprecated. Parse it, but
370 # let on_xyz override it if they are present.
371 restart = get_cfg('restart')
372 if restart:
373 def handle_restart(event, val):
374 if result[event] is None:
375 result[event] = val
377 if restart == "onreboot":
378 handle_restart('on_poweroff', 'destroy')
379 handle_restart('on_reboot', 'restart')
380 handle_restart('on_crash', 'destroy')
381 elif restart == "always":
382 handle_restart('on_poweroff', 'restart')
383 handle_restart('on_reboot', 'restart')
384 handle_restart('on_crash', 'restart')
385 elif restart == "never":
386 handle_restart('on_poweroff', 'destroy')
387 handle_restart('on_reboot', 'destroy')
388 handle_restart('on_crash', 'destroy')
389 else:
390 log.warn("Ignoring malformed and deprecated config option "
391 "restart = %s", restart)
393 log.debug("parseConfig: result is %s", result)
394 return result
397 def domain_by_name(name):
398 return XendDomain.instance().domain_lookup_by_name_nr(name)
401 def shutdown_reason(code):
402 """Get a shutdown reason from a code.
404 @param code: shutdown code
405 @type code: int
406 @return: shutdown reason
407 @rtype: string
408 """
409 return shutdown_reasons.get(code, "?")
411 def dom_get(dom):
412 """Get info from xen for an existing domain.
414 @param dom: domain id
415 @return: info or None
416 """
417 try:
418 domlist = xc.domain_getinfo(dom, 1)
419 if domlist and dom == domlist[0]['dom']:
420 return domlist[0]
421 except Exception, err:
422 # ignore missing domain
423 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
424 return None
427 class XendDomainInfo:
429 def __init__(self, info, domid = None, dompath = None, augment = False,
430 priv = False, resume = False):
432 self.info = info
434 if not self.infoIsSet('uuid'):
435 self.info['uuid'] = uuid.toString(uuid.create())
437 if domid is not None:
438 self.domid = domid
439 elif 'dom' in info:
440 self.domid = int(info['dom'])
441 else:
442 self.domid = None
444 self.vmpath = XendDomain.VMROOT + self.info['uuid']
445 self.dompath = dompath
447 if augment:
448 self.augmentInfo(priv)
450 self.validateInfo()
452 self.image = None
453 self.security = None
454 self.store_port = None
455 self.store_mfn = None
456 self.console_port = None
457 self.console_mfn = None
459 self.vmWatch = None
460 self.shutdownWatch = None
462 self.shutdownStartTime = None
464 self.state = STATE_DOM_OK
465 self.state_updated = threading.Condition()
466 self.refresh_shutdown_lock = threading.Condition()
468 self.setResume(resume)
470 ## private:
472 def readVMDetails(self, params):
473 """Read the specified parameters from the store.
474 """
475 try:
476 return self.gatherVm(*params)
477 except ValueError:
478 # One of the int/float entries in params has a corresponding store
479 # entry that is invalid. We recover, because older versions of
480 # Xend may have put the entry there (memory/target, for example),
481 # but this is in general a bad situation to have reached.
482 log.exception(
483 "Store corrupted at %s! Domain %d's configuration may be "
484 "affected.", self.vmpath, self.domid)
485 return []
488 def storeChanged(self, _):
489 log.trace("XendDomainInfo.storeChanged");
491 changed = False
493 def f(x, y):
494 if y is not None and self.info[x[0]] != y:
495 self.info[x[0]] = y
496 changed = True
498 map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
500 im = self.readVm('image')
501 current_im = self.info['image']
502 if (im is not None and
503 (current_im is None or sxp.to_string(current_im) != im)):
504 self.info['image'] = sxp.from_string(im)
505 changed = True
507 if changed:
508 # Update the domain section of the store, as this contains some
509 # parameters derived from the VM configuration.
510 self.storeDomDetails()
512 return 1
515 def augmentInfo(self, priv):
516 """Augment self.info, as given to us through {@link #recreate}, with
517 values taken from the store. This recovers those values known to xend
518 but not to the hypervisor.
519 """
520 def useIfNeeded(name, val):
521 if not self.infoIsSet(name) and val is not None:
522 self.info[name] = val
524 if priv:
525 entries = VM_STORE_ENTRIES[:]
526 entries.remove(('memory', int))
527 entries.remove(('maxmem', int))
528 else:
529 entries = VM_STORE_ENTRIES
530 entries.append(('image', str))
531 entries.append(('security', str))
533 map(lambda x, y: useIfNeeded(x[0], y), entries,
534 self.readVMDetails(entries))
536 device = []
537 for c in controllerClasses:
538 devconfig = self.getDeviceConfigurations(c)
539 if devconfig:
540 device.extend(map(lambda x: (c, x), devconfig))
541 useIfNeeded('device', device)
544 def validateInfo(self):
545 """Validate and normalise the info block. This has either been parsed
546 by parseConfig, or received from xc through recreate and augmented by
547 the current store contents.
548 """
549 def defaultInfo(name, val):
550 if not self.infoIsSet(name):
551 self.info[name] = val()
553 try:
554 defaultInfo('name', lambda: "Domain-%d" % self.domid)
555 defaultInfo('on_poweroff', lambda: "destroy")
556 defaultInfo('on_reboot', lambda: "restart")
557 defaultInfo('on_crash', lambda: "restart")
558 defaultInfo('features', lambda: "")
559 defaultInfo('cpu', lambda: None)
560 defaultInfo('cpus', lambda: [])
561 defaultInfo('cpu_weight', lambda: 1.0)
563 # some domains don't have a config file (e.g. dom0 )
564 # to set number of vcpus so we derive available cpus
565 # from max_vcpu_id which is present for running domains.
566 if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
567 avail = int(self.info['max_vcpu_id'])+1
568 else:
569 avail = int(1)
571 defaultInfo('vcpus', lambda: avail)
572 defaultInfo('online_vcpus', lambda: self.info['vcpus'])
573 defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
574 defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
576 defaultInfo('memory', lambda: 0)
577 defaultInfo('shadow_memory', lambda: 0)
578 defaultInfo('maxmem', lambda: 0)
579 defaultInfo('bootloader', lambda: None)
580 defaultInfo('bootloader_args', lambda: None)
581 defaultInfo('backend', lambda: [])
582 defaultInfo('device', lambda: [])
583 defaultInfo('image', lambda: None)
584 defaultInfo('security', lambda: None)
586 self.check_name(self.info['name'])
588 if isinstance(self.info['image'], str):
589 self.info['image'] = sxp.from_string(self.info['image'])
591 if isinstance(self.info['security'], str):
592 self.info['security'] = sxp.from_string(self.info['security'])
594 if self.info['memory'] == 0:
595 if self.infoIsSet('mem_kb'):
596 self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
598 if self.info['maxmem'] < self.info['memory']:
599 self.info['maxmem'] = self.info['memory']
601 for (n, c) in self.info['device']:
602 if not n or not c or n not in controllerClasses:
603 raise VmError('invalid device (%s, %s)' %
604 (str(n), str(c)))
606 for event in ['on_poweroff', 'on_reboot', 'on_crash']:
607 if self.info[event] not in restart_modes:
608 raise VmError('invalid restart event: %s = %s' %
609 (event, str(self.info[event])))
611 except KeyError, exn:
612 log.exception(exn)
613 raise VmError('Unspecified domain detail: %s' % exn)
616 def readVm(self, *args):
617 return xstransact.Read(self.vmpath, *args)
619 def writeVm(self, *args):
620 return xstransact.Write(self.vmpath, *args)
622 def removeVm(self, *args):
623 return xstransact.Remove(self.vmpath, *args)
625 def gatherVm(self, *args):
626 return xstransact.Gather(self.vmpath, *args)
629 ## public:
631 def storeVm(self, *args):
632 return xstransact.Store(self.vmpath, *args)
635 ## private:
637 def readDom(self, *args):
638 return xstransact.Read(self.dompath, *args)
640 def writeDom(self, *args):
641 return xstransact.Write(self.dompath, *args)
644 ## public:
646 def removeDom(self, *args):
647 return xstransact.Remove(self.dompath, *args)
649 def recreateDom(self):
650 complete(self.dompath, lambda t: self._recreateDom(t))
652 def _recreateDom(self, t):
653 t.remove()
654 t.mkdir()
655 t.set_permissions({ 'dom' : self.domid })
658 ## private:
660 def storeDom(self, *args):
661 return xstransact.Store(self.dompath, *args)
664 ## public:
666 def completeRestore(self, store_mfn, console_mfn):
668 log.debug("XendDomainInfo.completeRestore")
670 self.store_mfn = store_mfn
671 self.console_mfn = console_mfn
673 self.introduceDomain()
674 self.storeDomDetails()
675 self.registerWatches()
676 self.refreshShutdown()
678 log.debug("XendDomainInfo.completeRestore done")
681 def storeVmDetails(self):
682 to_store = {}
684 for k in VM_STORE_ENTRIES:
685 if self.infoIsSet(k[0]):
686 to_store[k[0]] = str(self.info[k[0]])
688 if self.infoIsSet('image'):
689 to_store['image'] = sxp.to_string(self.info['image'])
691 if self.infoIsSet('security'):
692 security = self.info['security']
693 to_store['security'] = sxp.to_string(security)
694 for idx in range(0, len(security)):
695 if security[idx][0] == 'access_control':
696 to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
697 for aidx in range(1, len(security[idx])):
698 if security[idx][aidx][0] == 'label':
699 to_store['security/access_control/label'] = security[idx][aidx][1]
700 if security[idx][aidx][0] == 'policy':
701 to_store['security/access_control/policy'] = security[idx][aidx][1]
702 if security[idx][0] == 'ssidref':
703 to_store['security/ssidref'] = str(security[idx][1])
705 if not self.readVm('xend/restart_count'):
706 to_store['xend/restart_count'] = str(0)
708 log.debug("Storing VM details: %s", to_store)
710 self.writeVm(to_store)
711 self.setVmPermissions()
714 def setVmPermissions(self):
715 """Allow the guest domain to read its UUID. We don't allow it to
716 access any other entry, for security."""
717 xstransact.SetPermissions('%s/uuid' % self.vmpath,
718 { 'dom' : self.domid,
719 'read' : True,
720 'write' : False })
723 def storeDomDetails(self):
724 to_store = {
725 'domid': str(self.domid),
726 'vm': self.vmpath,
727 'name': self.info['name'],
728 'console/limit': str(xroot.get_console_limit() * 1024),
729 'memory/target': str(self.info['memory'] * 1024)
730 }
732 def f(n, v):
733 if v is not None:
734 to_store[n] = str(v)
736 f('console/port', self.console_port)
737 f('console/ring-ref', self.console_mfn)
738 f('store/port', self.store_port)
739 f('store/ring-ref', self.store_mfn)
741 to_store.update(self.vcpuDomDetails())
743 log.debug("Storing domain details: %s", to_store)
745 self.writeDom(to_store)
748 ## private:
750 def vcpuDomDetails(self):
751 def availability(n):
752 if self.info['vcpu_avail'] & (1 << n):
753 return 'online'
754 else:
755 return 'offline'
757 result = {}
758 for v in range(0, self.info['vcpus']):
759 result["cpu/%d/availability" % v] = availability(v)
760 return result
763 ## public:
765 def registerWatches(self):
766 """Register a watch on this VM's entries in the store, and the
767 domain's control/shutdown node, so that when they are changed
768 externally, we keep up to date. This should only be called by {@link
769 #create}, {@link #recreate}, or {@link #restore}, once the domain's
770 details have been written, but before the new instance is returned."""
771 self.vmWatch = xswatch(self.vmpath, self.storeChanged)
772 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
773 self.handleShutdownWatch)
776 def getDomid(self):
777 return self.domid
779 def setName(self, name):
780 self.check_name(name)
781 self.info['name'] = name
782 self.storeVm("name", name)
784 def getName(self):
785 return self.info['name']
787 def getDomainPath(self):
788 return self.dompath
791 def getStorePort(self):
792 """For use only by image.py and XendCheckpoint.py."""
793 return self.store_port
796 def getConsolePort(self):
797 """For use only by image.py and XendCheckpoint.py"""
798 return self.console_port
800 def getFeatures(self):
801 """For use only by image.py."""
802 return self.info['features']
804 def getVCpuCount(self):
805 return self.info['vcpus']
808 def setVCpuCount(self, vcpus):
809 self.info['vcpu_avail'] = (1 << vcpus) - 1
810 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
811 self.writeDom(self.vcpuDomDetails())
813 def getLabel(self):
814 return security.get_security_info(self.info, 'label')
816 def getMemoryTarget(self):
817 """Get this domain's target memory size, in KB."""
818 return self.info['memory'] * 1024
820 def getResume(self):
821 return "%s" % self.info['resume']
823 def endRestore(self):
824 self.setResume(False)
826 def setResume(self, state):
827 self.info['resume'] = state
829 def getRestartCount(self):
830 return self.readVm('xend/restart_count')
832 def refreshShutdown(self, xeninfo = None):
833 # If set at the end of this method, a restart is required, with the
834 # given reason. This restart has to be done out of the scope of
835 # refresh_shutdown_lock.
836 restart_reason = None
838 self.refresh_shutdown_lock.acquire()
839 try:
840 if xeninfo is None:
841 xeninfo = dom_get(self.domid)
842 if xeninfo is None:
843 # The domain no longer exists. This will occur if we have
844 # scheduled a timer to check for shutdown timeouts and the
845 # shutdown succeeded. It will also occur if someone
846 # destroys a domain beneath us. We clean up the domain,
847 # just in case, but we can't clean up the VM, because that
848 # VM may have migrated to a different domain on this
849 # machine.
850 self.cleanupDomain()
851 return
853 if xeninfo['dying']:
854 # Dying means that a domain has been destroyed, but has not
855 # yet been cleaned up by Xen. This state could persist
856 # indefinitely if, for example, another domain has some of its
857 # pages mapped. We might like to diagnose this problem in the
858 # future, but for now all we do is make sure that it's not us
859 # holding the pages, by calling cleanupDomain. We can't
860 # clean up the VM, as above.
861 self.cleanupDomain()
862 return
864 elif xeninfo['crashed']:
865 if self.readDom('xend/shutdown_completed'):
866 # We've seen this shutdown already, but we are preserving
867 # the domain for debugging. Leave it alone.
868 return
870 log.warn('Domain has crashed: name=%s id=%d.',
871 self.info['name'], self.domid)
873 if xroot.get_enable_dump():
874 self.dumpCore()
876 restart_reason = 'crash'
878 elif xeninfo['shutdown']:
879 if self.readDom('xend/shutdown_completed'):
880 # We've seen this shutdown already, but we are preserving
881 # the domain for debugging. Leave it alone.
882 return
884 else:
885 reason = shutdown_reason(xeninfo['shutdown_reason'])
887 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
888 self.info['name'], self.domid, reason)
890 self.clearRestart()
892 if reason == 'suspend':
893 self.state_set(STATE_DOM_SHUTDOWN)
894 # Don't destroy the domain. XendCheckpoint will do
895 # this once it has finished. However, stop watching
896 # the VM path now, otherwise we will end up with one
897 # watch for the old domain, and one for the new.
898 self.unwatchVm()
899 elif reason in ['poweroff', 'reboot']:
900 restart_reason = reason
901 else:
902 self.destroy()
904 elif self.dompath is None:
905 # We have yet to manage to call introduceDomain on this
906 # domain. This can happen if a restore is in progress, or has
907 # failed. Ignore this domain.
908 pass
909 else:
910 # Domain is alive. If we are shutting it down, then check
911 # the timeout on that, and destroy it if necessary.
913 if self.shutdownStartTime:
914 timeout = (SHUTDOWN_TIMEOUT - time.time() +
915 self.shutdownStartTime)
916 if timeout < 0:
917 log.info(
918 "Domain shutdown timeout expired: name=%s id=%s",
919 self.info['name'], self.domid)
920 self.destroy()
921 finally:
922 self.refresh_shutdown_lock.release()
924 if restart_reason:
925 self.maybeRestart(restart_reason)
928 def handleShutdownWatch(self, _):
929 log.debug('XendDomainInfo.handleShutdownWatch')
931 reason = self.readDom('control/shutdown')
933 if reason and reason != 'suspend':
934 sst = self.readDom('xend/shutdown_start_time')
935 now = time.time()
936 if sst:
937 self.shutdownStartTime = float(sst)
938 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
939 else:
940 self.shutdownStartTime = now
941 self.storeDom('xend/shutdown_start_time', now)
942 timeout = SHUTDOWN_TIMEOUT
944 log.trace(
945 "Scheduling refreshShutdown on domain %d in %ds.",
946 self.domid, timeout)
947 threading.Timer(timeout, self.refreshShutdown).start()
949 return True
952 def shutdown(self, reason):
953 if not reason in shutdown_reasons.values():
954 raise XendError('Invalid reason: %s' % reason)
955 if self.domid == 0:
956 raise XendError("Can't specify Domain-0")
957 self.storeDom("control/shutdown", reason)
960 ## private:
962 def clearRestart(self):
963 self.removeDom("xend/shutdown_start_time")
966 def maybeRestart(self, reason):
967 # Dispatch to the correct method based upon the configured on_{reason}
968 # behaviour.
969 {"destroy" : self.destroy,
970 "restart" : self.restart,
971 "preserve" : self.preserve,
972 "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
975 def renameRestart(self):
976 self.restart(True)
979 def dumpCore(self):
980 """Create a core dump for this domain. Nothrow guarantee."""
982 try:
983 corefile = "/var/xen/dump/%s.%s.core" % (self.info['name'],
984 self.domid)
985 xc.domain_dumpcore(self.domid, corefile)
987 except:
988 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
989 self.domid, self.info['name'])
992 ## public:
994 def setMemoryTarget(self, target):
995 """Set the memory target of this domain.
996 @param target In MiB.
997 """
998 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
999 self.info['name'], self.domid, target)
1001 self.info['memory'] = target
1002 self.storeVm("memory", target)
1003 self.storeDom("memory/target", target << 10)
1006 def update(self, info = None):
1007 """Update with info from xc.domain_getinfo().
1008 """
1010 log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
1011 if not info:
1012 info = dom_get(self.domid)
1013 if not info:
1014 return
1016 #manually update ssidref / security fields
1017 if security.on() and info.has_key('ssidref'):
1018 if (info['ssidref'] != 0) and self.info.has_key('security'):
1019 security_field = self.info['security']
1020 if not security_field:
1021 #create new security element
1022 self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
1023 #ssidref field not used any longer
1024 info.pop('ssidref')
1026 self.info.update(info)
1027 self.validateInfo()
1028 self.refreshShutdown(info)
1030 log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
1031 self.info)
1034 ## private:
1036 def state_set(self, state):
1037 self.state_updated.acquire()
1038 try:
1039 if self.state != state:
1040 self.state = state
1041 self.state_updated.notifyAll()
1042 finally:
1043 self.state_updated.release()
1046 ## public:
1048 def waitForShutdown(self):
1049 self.state_updated.acquire()
1050 try:
1051 while self.state == STATE_DOM_OK:
1052 self.state_updated.wait()
1053 finally:
1054 self.state_updated.release()
1057 def __str__(self):
1058 s = "<domain"
1059 s += " id=" + str(self.domid)
1060 s += " name=" + self.info['name']
1061 s += " memory=" + str(self.info['memory'])
1062 s += ">"
1063 return s
1065 __repr__ = __str__
1068 ## private:
1070 def createDevice(self, deviceClass, devconfig):
1071 return self.getDeviceController(deviceClass).createDevice(devconfig)
1074 def waitForDevices_(self, deviceClass):
1075 return self.getDeviceController(deviceClass).waitForDevices()
1078 def waitForDevice(self, deviceClass, devid):
1079 return self.getDeviceController(deviceClass).waitForDevice(devid)
1082 def reconfigureDevice(self, deviceClass, devid, devconfig):
1083 return self.getDeviceController(deviceClass).reconfigureDevice(
1084 devid, devconfig)
1087 ## public:
1089 def destroyDevice(self, deviceClass, devid):
1090 return self.getDeviceController(deviceClass).destroyDevice(devid)
1093 def getDeviceSxprs(self, deviceClass):
1094 return self.getDeviceController(deviceClass).sxprs()
1097 ## private:
1099 def getDeviceConfigurations(self, deviceClass):
1100 return self.getDeviceController(deviceClass).configurations()
1103 def getDeviceController(self, name):
1104 if name not in controllerClasses:
1105 raise XendError("unknown device type: " + str(name))
1107 return controllerClasses[name](self)
1110 ## public:
1112 def sxpr(self):
1113 sxpr = ['domain',
1114 ['domid', self.domid]]
1116 for e in ROUNDTRIPPING_CONFIG_ENTRIES:
1117 if self.infoIsSet(e[0]):
1118 sxpr.append([e[0], self.info[e[0]]])
1120 if self.infoIsSet('image'):
1121 sxpr.append(['image', self.info['image']])
1123 if self.infoIsSet('security'):
1124 sxpr.append(['security', self.info['security']])
1126 for cls in controllerClasses:
1127 for config in self.getDeviceConfigurations(cls):
1128 sxpr.append(['device', config])
1130 def stateChar(name):
1131 if name in self.info:
1132 if self.info[name]:
1133 return name[0]
1134 else:
1135 return '-'
1136 else:
1137 return '?'
1139 state = reduce(
1140 lambda x, y: x + y,
1141 map(stateChar,
1142 ['running', 'blocked', 'paused', 'shutdown', 'crashed',
1143 'dying']))
1145 sxpr.append(['state', state])
1146 if self.infoIsSet('shutdown'):
1147 reason = shutdown_reason(self.info['shutdown_reason'])
1148 sxpr.append(['shutdown_reason', reason])
1149 if self.infoIsSet('cpu_time'):
1150 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
1151 sxpr.append(['online_vcpus', self.info['online_vcpus']])
1153 if self.infoIsSet('start_time'):
1154 up_time = time.time() - self.info['start_time']
1155 sxpr.append(['up_time', str(up_time) ])
1156 sxpr.append(['start_time', str(self.info['start_time']) ])
1158 if self.store_mfn:
1159 sxpr.append(['store_mfn', self.store_mfn])
1160 if self.console_mfn:
1161 sxpr.append(['console_mfn', self.console_mfn])
1163 return sxpr
1166 def getVCPUInfo(self):
1167 try:
1168 # We include the domain name and ID, to help xm.
1169 sxpr = ['domain',
1170 ['domid', self.domid],
1171 ['name', self.info['name']],
1172 ['vcpu_count', self.info['online_vcpus']]]
1174 for i in range(0, self.info['max_vcpu_id']+1):
1175 info = xc.vcpu_getinfo(self.domid, i)
1177 sxpr.append(['vcpu',
1178 ['number', i],
1179 ['online', info['online']],
1180 ['blocked', info['blocked']],
1181 ['running', info['running']],
1182 ['cpu_time', info['cpu_time'] / 1e9],
1183 ['cpu', info['cpu']],
1184 ['cpumap', info['cpumap']]])
1186 return sxpr
1188 except RuntimeError, exn:
1189 raise XendError(str(exn))
1192 ## private:
1194 def check_name(self, name):
1195 """Check if a vm name is valid. Valid names contain alphabetic characters,
1196 digits, or characters in '_-.:/+'.
1197 The same name cannot be used for more than one vm at the same time.
1199 @param name: name
1200 @raise: VmError if invalid
1201 """
1202 if name is None or name == '':
1203 raise VmError('missing vm name')
1204 for c in name:
1205 if c in string.digits: continue
1206 if c in '_-.:/+': continue
1207 if c in string.ascii_letters: continue
1208 raise VmError('invalid vm name')
1210 dominfo = domain_by_name(name)
1211 if not dominfo:
1212 return
1213 if self.domid is None:
1214 raise VmError("VM name '%s' already in use by domain %d" %
1215 (name, dominfo.domid))
1216 if dominfo.domid != self.domid:
1217 raise VmError("VM name '%s' is used in both domains %d and %d" %
1218 (name, self.domid, dominfo.domid))
1221 def construct(self):
1222 """Construct the domain.
1224 @raise: VmError on error
1225 """
1227 log.debug('XendDomainInfo.construct: %s',
1228 self.domid)
1230 self.domid = xc.domain_create(
1231 dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
1232 handle = uuid.fromString(self.info['uuid']))
1234 if self.domid < 0:
1235 raise VmError('Creating domain failed: name=%s' %
1236 self.info['name'])
1238 self.dompath = GetDomainPath(self.domid)
1240 self.recreateDom()
1242 # Set maximum number of vcpus in domain
1243 xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
1246 def introduceDomain(self):
1247 assert self.domid is not None
1248 assert self.store_mfn is not None
1249 assert self.store_port is not None
1251 try:
1252 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1253 except RuntimeError, exn:
1254 raise XendError(str(exn))
1257 def initDomain(self):
1258 log.debug('XendDomainInfo.initDomain: %s %s',
1259 self.domid,
1260 self.info['cpu_weight'])
1262 # if we have a boot loader but no image, then we need to set things
1263 # up by running the boot loader non-interactively
1264 if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
1265 self.configure_bootloader()
1267 if not self.infoIsSet('image'):
1268 raise VmError('Missing image in configuration')
1270 try:
1271 self.image = image.create(self,
1272 self.info['image'],
1273 self.info['device'])
1275 localtime = self.info['localtime']
1276 if localtime is not None and localtime == 1:
1277 xc.domain_set_time_offset(self.domid)
1279 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1281 # repin domain vcpus if a restricted cpus list is provided
1282 # this is done prior to memory allocation to aide in memory
1283 # distribution for NUMA systems.
1284 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1285 for v in range(0, self.info['max_vcpu_id']+1):
1286 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1288 # set memory limit
1289 maxmem = self.image.getRequiredMemory(self.info['maxmem'] * 1024)
1290 xc.domain_setmaxmem(self.domid, maxmem)
1292 mem_kb = self.image.getRequiredMemory(self.info['memory'] * 1024)
1294 # get the domain's shadow memory requirement
1295 shadow_kb = self.image.getRequiredShadowMemory(mem_kb)
1296 shadow_kb_req = self.info['shadow_memory'] * 1024
1297 if shadow_kb_req > shadow_kb:
1298 shadow_kb = shadow_kb_req
1300 # Make sure there's enough RAM available for the domain
1301 balloon.free(mem_kb + shadow_kb)
1303 # Set up the shadow memory
1304 shadow_cur = xc.shadow_mem_control(self.domid,
1305 (shadow_kb + 1023) / 1024)
1306 self.info['shadow_memory'] = shadow_cur
1308 # initial memory allocation
1309 xc.domain_memory_increase_reservation(self.domid, mem_kb, 0, 0)
1311 self.createChannels()
1313 channel_details = self.image.createImage()
1315 self.store_mfn = channel_details['store_mfn']
1316 if 'console_mfn' in channel_details:
1317 self.console_mfn = channel_details['console_mfn']
1319 self.introduceDomain()
1321 self.createDevices()
1323 if self.info['bootloader']:
1324 self.image.cleanupBootloading()
1326 self.info['start_time'] = time.time()
1328 except RuntimeError, exn:
1329 raise VmError(str(exn))
1332 ## public:
1334 def cleanupDomain(self):
1335 """Cleanup domain resources; release devices. Idempotent. Nothrow
1336 guarantee."""
1338 self.refresh_shutdown_lock.acquire()
1339 try:
1340 self.unwatchShutdown()
1342 self.release_devices()
1344 if self.image:
1345 try:
1346 self.image.destroy()
1347 except:
1348 log.exception(
1349 "XendDomainInfo.cleanup: image.destroy() failed.")
1350 self.image = None
1352 try:
1353 self.removeDom()
1354 except:
1355 log.exception("Removing domain path failed.")
1357 try:
1358 if not self.info['name'].startswith(ZOMBIE_PREFIX):
1359 self.info['name'] = ZOMBIE_PREFIX + self.info['name']
1360 except:
1361 log.exception("Renaming Zombie failed.")
1363 self.state_set(STATE_DOM_SHUTDOWN)
1364 finally:
1365 self.refresh_shutdown_lock.release()
1368 def cleanupVm(self):
1369 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1371 self.unwatchVm()
1373 try:
1374 self.removeVm()
1375 except:
1376 log.exception("Removing VM path failed.")
1379 ## private:
1381 def unwatchVm(self):
1382 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1383 guarantee."""
1385 try:
1386 try:
1387 if self.vmWatch:
1388 self.vmWatch.unwatch()
1389 finally:
1390 self.vmWatch = None
1391 except:
1392 log.exception("Unwatching VM path failed.")
1395 def unwatchShutdown(self):
1396 """Remove the watch on the domain's control/shutdown node, if any.
1397 Idempotent. Nothrow guarantee. Expects to be protected by the
1398 refresh_shutdown_lock."""
1400 try:
1401 try:
1402 if self.shutdownWatch:
1403 self.shutdownWatch.unwatch()
1404 finally:
1405 self.shutdownWatch = None
1406 except:
1407 log.exception("Unwatching control/shutdown failed.")
1410 ## public:
1412 def destroy(self):
1413 """Cleanup VM and destroy domain. Nothrow guarantee."""
1415 log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
1417 self.cleanupVm()
1418 if self.dompath is not None:
1419 self.destroyDomain()
1422 def destroyDomain(self):
1423 log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
1425 try:
1426 if self.domid is not None:
1427 xc.domain_destroy(self.domid)
1428 except:
1429 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1431 self.cleanupDomain()
1434 ## private:
1436 def release_devices(self):
1437 """Release all domain's devices. Nothrow guarantee."""
1439 while True:
1440 t = xstransact("%s/device" % self.dompath)
1441 for n in controllerClasses.keys():
1442 for d in t.list(n):
1443 try:
1444 t.remove(d)
1445 except:
1446 # Log and swallow any exceptions in removal --
1447 # there's nothing more we can do.
1448 log.exception(
1449 "Device release failed: %s; %s; %s",
1450 self.info['name'], n, d)
1451 if t.commit():
1452 break
1455 def createChannels(self):
1456 """Create the channels to the domain.
1457 """
1458 self.store_port = self.createChannel()
1459 self.console_port = self.createChannel()
1462 def createChannel(self):
1463 """Create an event channel to the domain.
1464 """
1465 try:
1466 return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
1467 except:
1468 log.exception("Exception in alloc_unbound(%d)", self.domid)
1469 raise
1472 ## public:
1474 def createDevices(self):
1475 """Create the devices for a vm.
1477 @raise: VmError for invalid devices
1478 """
1480 for (n, c) in self.info['device']:
1481 self.createDevice(n, c)
1483 if self.image:
1484 self.image.createDeviceModel()
1486 ## public:
1488 def testMigrateDevices(self, network, dst):
1489 """ Notify all device about intention of migration
1490 @raise: XendError for a device that cannot be migrated
1491 """
1492 for (n, c) in self.info['device']:
1493 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1494 if rc != 0:
1495 raise XendError("Device of type '%s' refuses migration." % n)
1497 def migrateDevices(self, network, dst, step, domName=''):
1498 """Notify the devices about migration
1499 """
1500 ctr = 0
1501 try:
1502 for (n, c) in self.info['device']:
1503 self.migrateDevice(n, c, network, dst, step, domName)
1504 ctr = ctr + 1
1505 except:
1506 for (n, c) in self.info['device']:
1507 if ctr == 0:
1508 step = step - 1
1509 ctr = ctr - 1
1510 self.recoverMigrateDevice(n, c, network, dst, step, domName)
1511 raise
1513 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1514 step, domName=''):
1515 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1516 network, dst, step, domName)
1518 def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1519 dst, step, domName=''):
1520 return self.getDeviceController(deviceClass).recover_migrate(
1521 deviceConfig, network, dst, step, domName)
1523 def waitForDevices(self):
1524 """Wait for this domain's configured devices to connect.
1526 @raise: VmError if any device fails to initialise.
1527 """
1528 for c in controllerClasses:
1529 self.waitForDevices_(c)
1532 def device_create(self, dev_config):
1533 """Create a new device.
1535 @param dev_config: device configuration
1536 """
1537 dev_type = sxp.name(dev_config)
1538 devid = self.createDevice(dev_type, dev_config)
1539 self.waitForDevice(dev_type, devid)
1540 self.info['device'].append((dev_type, dev_config))
1541 return self.getDeviceController(dev_type).sxpr(devid)
1544 def device_configure(self, dev_config):
1545 """Configure an existing device.
1546 @param dev_config: device configuration
1547 """
1548 deviceClass = sxp.name(dev_config)
1549 self.reconfigureDevice(deviceClass, None, dev_config)
1552 def pause(self):
1553 xc.domain_pause(self.domid)
1556 def unpause(self):
1557 xc.domain_unpause(self.domid)
1560 ## private:
1562 def restart(self, rename = False):
1563 """Restart the domain after it has exited.
1565 @param rename True if the old domain is to be renamed and preserved,
1566 False if it is to be destroyed.
1567 """
1569 self.configure_bootloader()
1570 config = self.sxpr()
1572 if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
1573 config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
1574 self.info['cpus'])])
1576 if self.readVm(RESTART_IN_PROGRESS):
1577 log.error('Xend failed during restart of domain %d. '
1578 'Refusing to restart to avoid loops.',
1579 self.domid)
1580 self.destroy()
1581 return
1583 self.writeVm(RESTART_IN_PROGRESS, 'True')
1585 now = time.time()
1586 rst = self.readVm('xend/previous_restart_time')
1587 if rst:
1588 rst = float(rst)
1589 timeout = now - rst
1590 if timeout < MINIMUM_RESTART_TIME:
1591 log.error(
1592 'VM %s restarting too fast (%f seconds since the last '
1593 'restart). Refusing to restart to avoid loops.',
1594 self.info['name'], timeout)
1595 self.destroy()
1596 return
1598 self.writeVm('xend/previous_restart_time', str(now))
1600 try:
1601 if rename:
1602 self.preserveForRestart()
1603 else:
1604 self.unwatchVm()
1605 self.destroyDomain()
1607 # new_dom's VM will be the same as this domain's VM, except where
1608 # the rename flag has instructed us to call preserveForRestart.
1609 # In that case, it is important that we remove the
1610 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1611 # once the new one is available.
1613 new_dom = None
1614 try:
1615 new_dom = XendDomain.instance().domain_create(config)
1616 new_dom.unpause()
1617 rst_cnt = self.readVm('xend/restart_count')
1618 rst_cnt = int(rst_cnt) + 1
1619 self.writeVm('xend/restart_count', str(rst_cnt))
1620 new_dom.removeVm(RESTART_IN_PROGRESS)
1621 except:
1622 if new_dom:
1623 new_dom.removeVm(RESTART_IN_PROGRESS)
1624 new_dom.destroy()
1625 else:
1626 self.removeVm(RESTART_IN_PROGRESS)
1627 raise
1628 except:
1629 log.exception('Failed to restart domain %d.', self.domid)
1632 def preserveForRestart(self):
1633 """Preserve a domain that has been shut down, by giving it a new UUID,
1634 cloning the VM details, and giving it a new name. This allows us to
1635 keep this domain for debugging, but restart a new one in its place
1636 preserving the restart semantics (name and UUID preserved).
1637 """
1639 new_name = self.generateUniqueName()
1640 new_uuid = uuid.toString(uuid.create())
1641 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1642 self.info['name'], self.domid, self.info['uuid'],
1643 new_name, new_uuid)
1644 self.unwatchVm()
1645 self.release_devices()
1646 self.info['name'] = new_name
1647 self.info['uuid'] = new_uuid
1648 self.vmpath = XendDomain.VMROOT + new_uuid
1649 self.storeVmDetails()
1650 self.preserve()
1653 def preserve(self):
1654 log.info("Preserving dead domain %s (%d).", self.info['name'],
1655 self.domid)
1656 self.unwatchVm()
1657 self.storeDom('xend/shutdown_completed', 'True')
1658 self.state_set(STATE_DOM_SHUTDOWN)
1661 # private:
1663 def generateUniqueName(self):
1664 n = 1
1665 while True:
1666 name = "%s-%d" % (self.info['name'], n)
1667 try:
1668 self.check_name(name)
1669 return name
1670 except VmError:
1671 n += 1
1674 def configure_bootloader(self):
1675 """Run the bootloader if we're configured to do so."""
1676 if not self.info['bootloader']:
1677 return
1678 blcfg = None
1679 # FIXME: this assumes that we want to use the first disk device
1680 for (n,c) in self.info['device']:
1681 if not n or not c or n != "vbd":
1682 continue
1683 disk = sxp.child_value(c, "uname")
1684 if disk is None:
1685 continue
1686 fn = blkdev_uname_to_file(disk)
1687 blcfg = bootloader(self.info['bootloader'], fn, 1,
1688 self.info['bootloader_args'],
1689 self.info['image'])
1690 break
1691 if blcfg is None:
1692 msg = "Had a bootloader specified, but can't find disk"
1693 log.error(msg)
1694 raise VmError(msg)
1695 self.info['image'] = blcfg
1698 def send_sysrq(self, key):
1699 asserts.isCharConvertible(key)
1701 self.storeDom("control/sysrq", '%c' % key)
1704 def infoIsSet(self, name):
1705 return name in self.info and self.info[name] is not None
1708 #============================================================================
1709 # Register device controllers and their device config types.
1711 """A map from device-class names to the subclass of DevController that
1712 implements the device control specific to that device-class."""
1713 controllerClasses = {}
1715 def addControllerClass(device_class, cls):
1716 """Register a subclass of DevController to handle the named device-class.
1717 """
1718 cls.deviceClass = device_class
1719 controllerClasses[device_class] = cls
1722 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
1723 from xen.xend.server.BlktapController import BlktapController
1724 addControllerClass('vbd', blkif.BlkifController)
1725 addControllerClass('vif', netif.NetifController)
1726 addControllerClass('vtpm', tpmif.TPMifController)
1727 addControllerClass('pci', pciif.PciController)
1728 addControllerClass('ioports', iopif.IOPortsController)
1729 addControllerClass('irq', irqif.IRQController)
1730 addControllerClass('usb', usbif.UsbifController)
1731 addControllerClass('tap', BlktapController)