direct-io.hg
changeset 7061:f9296342d9b2
Detangle the restart/reboot/halt/save/restore code. There is now one point
for management of shutdown state, XendDomainInfo.refreshShutdown, which is
able to cope whatever the current state of the domain. This fixes bug #124:
running reboot within a domU doesn't bring the domain back up after shutdown,
and bug #256: "xm reboot" could not make domU reboot.
Fix the refreshing inside XendDomain to ensure that the values returned by xm
list reflect reality correctly. We were removing XendDomainInfo instances,
but not creating them on start-up, so if xend were restarted, domain
information was being lost.
Merge XendDomain.dom0_unknown and initial_refresh into
XendDomainInfo.recreate.
Catch exceptions inside callInfo, and reraise them as XendErrors.
Remove unused XendDomain.close.
Further improvements are needed to XendDomainInfo to ensure that it avoids all
race conditions on shutdown, and can cope correctly with xend restarting
during a shutdown.
Signed-off-by: Ewan Mellor <ewan@xensource.com>
for management of shutdown state, XendDomainInfo.refreshShutdown, which is
able to cope whatever the current state of the domain. This fixes bug #124:
running reboot within a domU doesn't bring the domain back up after shutdown,
and bug #256: "xm reboot" could not make domU reboot.
Fix the refreshing inside XendDomain to ensure that the values returned by xm
list reflect reality correctly. We were removing XendDomainInfo instances,
but not creating them on start-up, so if xend were restarted, domain
information was being lost.
Merge XendDomain.dom0_unknown and initial_refresh into
XendDomainInfo.recreate.
Catch exceptions inside callInfo, and reraise them as XendErrors.
Remove unused XendDomain.close.
Further improvements are needed to XendDomainInfo to ensure that it avoids all
race conditions on shutdown, and can cope correctly with xend restarting
during a shutdown.
Signed-off-by: Ewan Mellor <ewan@xensource.com>
author | emellor@ewan |
---|---|
date | Tue Sep 27 13:53:06 2005 +0100 (2005-09-27) |
parents | 299c2d5b5daf |
children | 9ff1bea68d51 |
files | tools/python/xen/xend/XendDomain.py tools/python/xen/xend/XendDomainInfo.py |
line diff
1.1 --- a/tools/python/xen/xend/XendDomain.py Tue Sep 27 12:30:13 2005 +0100 1.2 +++ b/tools/python/xen/xend/XendDomain.py Tue Sep 27 13:53:06 2005 +0100 1.3 @@ -34,10 +34,8 @@ from xen.xend.XendError import XendError 1.4 from xen.xend.XendLogging import log 1.5 from xen.xend import scheduler 1.6 from xen.xend.server import relocate 1.7 -from xen.xend.uuid import getUuid 1.8 from xen.xend.xenstore import XenNode, DBMap 1.9 from xen.xend.xenstore.xstransact import xstransact 1.10 -from xen.xend.xenstore.xsutil import GetDomainPath 1.11 1.12 1.13 xc = xen.lowlevel.xc.new() 1.14 @@ -47,14 +45,7 @@ eserver = EventServer.instance() 1.15 1.16 __all__ = [ "XendDomain" ] 1.17 1.18 -SHUTDOWN_TIMEOUT = 30 1.19 -PRIV_DOMAIN = 0 1.20 - 1.21 -def is_dead(dom): 1.22 - return dom['crashed'] or dom['shutdown'] or ( 1.23 - dom['dying'] and not(dom['running'] or dom['paused'] or 1.24 - dom['blocked'])) 1.25 - 1.26 +PRIV_DOMAIN = 0 1.27 1.28 class XendDomainDict(dict): 1.29 def get_by_name(self, name): 1.30 @@ -77,11 +68,10 @@ class XendDomain: 1.31 # So we stuff the XendDomain instance (self) into xroot's components. 1.32 xroot.add_component("xen.xend.XendDomain", self) 1.33 self.domains = XendDomainDict() 1.34 - self.domroot = "/domain" 1.35 self.vmroot = "/domain" 1.36 self.dbmap = DBMap(db=XenNode(self.vmroot)) 1.37 self.watchReleaseDomain() 1.38 - self.initial_refresh() 1.39 + self.refresh() 1.40 self.dom0_setup() 1.41 1.42 def list(self): 1.43 @@ -110,9 +100,7 @@ class XendDomain: 1.44 return map(lambda x: x.getName(), doms) 1.45 1.46 def onReleaseDomain(self): 1.47 - self.reap() 1.48 self.refresh() 1.49 - self.domain_restarts() 1.50 1.51 def watchReleaseDomain(self): 1.52 from xen.xend.xenstore.xswatch import xswatch 1.53 @@ -141,43 +129,22 @@ class XendDomain: 1.54 dominfo = dominfo[0] 1.55 return dominfo 1.56 1.57 - def initial_refresh(self): 1.58 - """Refresh initial domain info from db. 1.59 - """ 1.60 - doms = self.xen_domains() 1.61 - self.dbmap.readDB() # XXX only needed for "xend" 1.62 - for dom in doms.values(): 1.63 - domid = dom['dom'] 1.64 - dompath = GetDomainPath(domid) 1.65 - if not dompath: 1.66 - continue 1.67 - vmpath = xstransact.Read(dompath, "vm") 1.68 - if not vmpath: 1.69 - continue 1.70 - uuid = xstransact.Read(vmpath, "uuid") 1.71 - if not uuid: 1.72 - continue 1.73 - log.info("recreating domain %d, uuid %s" % (domid, uuid)) 1.74 - dompath = "/".join(dompath.split("/")[0:-1]) 1.75 - try: 1.76 - dominfo = XendDomainInfo.recreate(uuid, dompath, domid, dom) 1.77 - except Exception, ex: 1.78 - log.exception("Error recreating domain info: id=%d", domid) 1.79 - continue 1.80 - self._add_domain(dominfo) 1.81 - self.reap() 1.82 - self.refresh() 1.83 - self.domain_restarts() 1.84 + 1.85 + def recreate_domain(self, xeninfo): 1.86 + """Refresh initial domain info from db.""" 1.87 + 1.88 + dominfo = XendDomainInfo.recreate(xeninfo) 1.89 + self._add_domain(dominfo) 1.90 + return dominfo 1.91 + 1.92 1.93 def dom0_setup(self): 1.94 dom0 = self.domain_lookup(PRIV_DOMAIN) 1.95 if not dom0: 1.96 - dom0 = self.dom0_unknown() 1.97 - dom0.dom0_init_store() 1.98 + dom0 = self.recreate_domain(self.xen_domain(PRIV_DOMAIN)) 1.99 + dom0.dom0_init_store() 1.100 dom0.dom0_enforce_vcpus() 1.101 1.102 - def close(self): 1.103 - pass 1.104 1.105 def _add_domain(self, info, notify=True): 1.106 """Add a domain entry to the tables. 1.107 @@ -219,44 +186,30 @@ class XendDomain: 1.108 if (domid is None) or (domid == id): 1.109 domdb.delete() 1.110 1.111 - def reap(self): 1.112 - """Look for domains that have crashed or stopped. 1.113 - Tidy them up. 1.114 - """ 1.115 - doms = self.xen_domains() 1.116 - for d in doms.values(): 1.117 - if not is_dead(d): 1.118 - continue 1.119 - domid = d['dom'] 1.120 - dominfo = self.domains.get(domid) 1.121 - if not dominfo or dominfo.is_terminated(): 1.122 - continue 1.123 - log.debug('domain died name=%s domid=%d', dominfo.getName(), domid) 1.124 - if d['crashed'] and xroot.get_enable_dump(): 1.125 - self.domain_dumpcore(domid) 1.126 - if d['shutdown']: 1.127 - reason = shutdown_reason(d['shutdown_reason']) 1.128 - log.debug('shutdown name=%s id=%d reason=%s', 1.129 - dominfo.getName(), domid, reason) 1.130 - if reason == 'suspend': 1.131 - dominfo.state_set("suspended") 1.132 - continue 1.133 - if reason in ['poweroff', 'reboot']: 1.134 - self.domain_restart_schedule(domid, reason) 1.135 - dominfo.destroy() 1.136 1.137 def refresh(self): 1.138 """Refresh domain list from Xen. 1.139 """ 1.140 doms = self.xen_domains() 1.141 - # Remove entries for domains that no longer exist. 1.142 - # Update entries for existing domains. 1.143 for d in self.domains.values(): 1.144 info = doms.get(d.getDomid()) 1.145 if info: 1.146 d.update(info) 1.147 - elif not d.restart_pending(): 1.148 + else: 1.149 self._delete_domain(d.getDomid()) 1.150 + for d in doms: 1.151 + if d not in self.domains: 1.152 + try: 1.153 + self.recreate_domain(doms[d]) 1.154 + except: 1.155 + log.exception( 1.156 + "Failed to recreate information for domain %d. " 1.157 + "Destroying it in the hope of recovery.", d) 1.158 + try: 1.159 + xc.domain_destroy(dom = d) 1.160 + except: 1.161 + log.exception('Destruction of %d failed.', d) 1.162 + 1.163 1.164 def update_domain(self, id): 1.165 """Update information for a single domain. 1.166 @@ -281,30 +234,6 @@ class XendDomain: 1.167 self._add_domain(dominfo) 1.168 return dominfo 1.169 1.170 - def domain_restart(self, dominfo): 1.171 - """Restart a domain. 1.172 - 1.173 - @param dominfo: domain object 1.174 - """ 1.175 - log.info("Restarting domain: name=%s id=%s", dominfo.getName(), 1.176 - dominfo.getDomid()) 1.177 - eserver.inject("xend.domain.restart", 1.178 - [dominfo.getName(), dominfo.getDomid(), "begin"]) 1.179 - try: 1.180 - dominfo.restart() 1.181 - log.info('Restarted domain name=%s id=%s', dominfo.getName(), 1.182 - dominfo.getDomid()) 1.183 - eserver.inject("xend.domain.restart", 1.184 - [dominfo.getName(), dominfo.getDomid(), 1.185 - "success"]) 1.186 - self.domain_unpause(dominfo.getDomid()) 1.187 - except Exception, ex: 1.188 - log.exception("Exception restarting domain: name=%s id=%s", 1.189 - dominfo.getName(), dominfo.getDomid()) 1.190 - eserver.inject("xend.domain.restart", 1.191 - [dominfo.getName(), dominfo.getDomid(), "fail"]) 1.192 - return dominfo 1.193 - 1.194 def domain_configure(self, config): 1.195 """Configure an existing domain. This is intended for internal 1.196 use by domain restore and migrate. 1.197 @@ -345,33 +274,7 @@ class XendDomain: 1.198 self.update_domain(id) 1.199 return self.domains.get(id) 1.200 1.201 - def dom0_unknown(self): 1.202 - dom0 = PRIV_DOMAIN 1.203 - uuid = None 1.204 - info = self.xen_domain(dom0) 1.205 - dompath = GetDomainPath(dom0) 1.206 - if dompath: 1.207 - vmpath = xstransact.Read(dompath, "vm") 1.208 - if vmpath: 1.209 - uuid = xstransact.Read(vmpath, "uuid") 1.210 - if not uuid: 1.211 - uuid = dompath.split("/")[-1] 1.212 - dompath = "/".join(dompath.split("/")[0:-1]) 1.213 - if not uuid: 1.214 - uuid = getUuid() 1.215 - dompath = self.domroot 1.216 - log.info("Creating entry for unknown xend domain: id=%d uuid=%s", 1.217 - dom0, uuid) 1.218 - try: 1.219 - dominfo = XendDomainInfo.recreate(uuid, dompath, dom0, info) 1.220 - self._add_domain(dominfo) 1.221 - return dominfo 1.222 - except Exception, exn: 1.223 - log.exception(exn) 1.224 - raise XendError("Error recreating xend domain info: id=%d: %s" % 1.225 - (dom0, str(exn))) 1.226 1.227 - 1.228 def domain_lookup(self, id): 1.229 return self.domains.get(id) 1.230 1.231 @@ -410,8 +313,9 @@ class XendDomain: 1.232 return xc.domain_pause(dom=dominfo.getDomid()) 1.233 except Exception, ex: 1.234 raise XendError(str(ex)) 1.235 - 1.236 - def domain_shutdown(self, id, reason='poweroff'): 1.237 + 1.238 + 1.239 + def domain_shutdown(self, domid, reason='poweroff'): 1.240 """Shutdown domain (nicely). 1.241 - poweroff: restart according to exit code and restart mode 1.242 - reboot: restart on exit 1.243 @@ -422,89 +326,13 @@ class XendDomain: 1.244 @param id: domain id 1.245 @param reason: shutdown type: poweroff, reboot, suspend, halt 1.246 """ 1.247 - dominfo = self.domain_lookup(id) 1.248 - self.domain_restart_schedule(dominfo.getDomid(), reason, force=True) 1.249 - eserver.inject('xend.domain.shutdown', [dominfo.getName(), 1.250 - dominfo.getDomid(), reason]) 1.251 - if reason == 'halt': 1.252 - reason = 'poweroff' 1.253 - val = dominfo.shutdown(reason) 1.254 - if not reason in ['suspend']: 1.255 - self.domain_shutdowns() 1.256 - return val 1.257 - 1.258 - 1.259 - def domain_sysrq(self, id, key): 1.260 - """Send a SysRq to the specified domain.""" 1.261 - return self.callInfo(id, XendDomainInfo.send_sysrq, key) 1.262 + self.callInfo(domid, XendDomainInfo.shutdown, reason) 1.263 1.264 1.265 - def domain_shutdowns(self): 1.266 - """Process pending domain shutdowns. 1.267 - Destroys domains whose shutdowns have timed out. 1.268 - """ 1.269 - timeout = SHUTDOWN_TIMEOUT + 1 1.270 - for dominfo in self.domains.values(): 1.271 - if not dominfo.shutdown_pending: 1.272 - # domain doesn't need shutdown 1.273 - continue 1.274 - id = dominfo.getDomid() 1.275 - left = dominfo.shutdown_time_left(SHUTDOWN_TIMEOUT) 1.276 - if left <= 0: 1.277 - # Shutdown expired - destroy domain. 1.278 - try: 1.279 - log.info("Domain shutdown timeout expired: name=%s id=%s", 1.280 - dominfo.getName(), id) 1.281 - self.domain_destroy(id, reason= 1.282 - dominfo.shutdown_pending['reason']) 1.283 - except Exception: 1.284 - pass 1.285 - else: 1.286 - # Shutdown still pending. 1.287 - timeout = min(timeout, left) 1.288 - if timeout <= SHUTDOWN_TIMEOUT: 1.289 - # Pending shutdowns remain - reschedule. 1.290 - scheduler.later(timeout, self.domain_shutdowns) 1.291 - 1.292 - def domain_restart_schedule(self, id, reason, force=False): 1.293 - """Schedule a restart for a domain if it needs one. 1.294 + def domain_sysrq(self, domid, key): 1.295 + """Send a SysRq to the specified domain.""" 1.296 + return self.callInfo(domid, XendDomainInfo.send_sysrq, key) 1.297 1.298 - @param id: domain id 1.299 - @param reason: shutdown reason 1.300 - """ 1.301 - log.debug('domain_restart_schedule> %d %s %d', id, reason, force) 1.302 - dominfo = self.domain_lookup(id) 1.303 - if not dominfo: 1.304 - return 1.305 - restart = (force and reason == 'reboot') or dominfo.restart_needed(reason) 1.306 - if restart: 1.307 - log.info('Scheduling restart for domain: name=%s id=%s', 1.308 - dominfo.getName(), dominfo.getDomid()) 1.309 - eserver.inject("xend.domain.restart", 1.310 - [dominfo.getName(), dominfo.getDomid(), 1.311 - "schedule"]) 1.312 - dominfo.restarting() 1.313 - else: 1.314 - log.info('Cancelling restart for domain: name=%s id=%s', 1.315 - dominfo.getName(), dominfo.getDomid()) 1.316 - eserver.inject("xend.domain.restart", 1.317 - [dominfo.getName(), dominfo.getDomid(), "cancel"]) 1.318 - dominfo.restart_cancel() 1.319 - 1.320 - def domain_restarts(self): 1.321 - """Execute any scheduled domain restarts for domains that have gone. 1.322 - """ 1.323 - doms = self.xen_domains() 1.324 - for dominfo in self.domains.values(): 1.325 - if not dominfo.restart_pending(): 1.326 - continue 1.327 - info = doms.get(dominfo.getDomid()) 1.328 - if info: 1.329 - # Don't execute restart for domains still running. 1.330 - continue 1.331 - # Remove it from the restarts. 1.332 - log.info('restarting: %s' % dominfo.getName()) 1.333 - self.domain_restart(dominfo) 1.334 1.335 def domain_destroy(self, domid, reason='halt'): 1.336 """Terminate domain immediately. 1.337 @@ -517,7 +345,6 @@ class XendDomain: 1.338 if domid == PRIV_DOMAIN: 1.339 raise XendError("Cannot destroy privileged domain %i" % domid) 1.340 1.341 - self.domain_restart_schedule(domid, reason, force=True) 1.342 dominfo = self.domain_lookup(domid) 1.343 if dominfo: 1.344 val = dominfo.destroy() 1.345 @@ -730,10 +557,15 @@ class XendDomain: 1.346 ## private: 1.347 1.348 def callInfo(self, domid, fn, *args, **kwargs): 1.349 - self.refresh() 1.350 - dominfo = self.domains.get(domid) 1.351 - if dominfo: 1.352 - return fn(dominfo, *args, **kwargs) 1.353 + try: 1.354 + self.refresh() 1.355 + dominfo = self.domains.get(domid) 1.356 + if dominfo: 1.357 + return fn(dominfo, *args, **kwargs) 1.358 + except XendError: 1.359 + raise 1.360 + except Exception, exn: 1.361 + raise XendError(str(exn)) 1.362 1.363 1.364 def instance():
2.1 --- a/tools/python/xen/xend/XendDomainInfo.py Tue Sep 27 12:30:13 2005 +0100 2.2 +++ b/tools/python/xen/xend/XendDomainInfo.py Tue Sep 27 13:53:06 2005 +0100 2.3 @@ -35,7 +35,9 @@ from xen.util.blkif import blkdev_uname_ 2.4 from xen.xend.server.channel import EventChannel 2.5 2.6 from xen.xend import image 2.7 +from xen.xend import scheduler 2.8 from xen.xend import sxp 2.9 +from xen.xend import XendRoot 2.10 from xen.xend.XendBootloader import bootloader 2.11 from xen.xend.XendLogging import log 2.12 from xen.xend.XendError import XendError, VmError 2.13 @@ -43,7 +45,7 @@ from xen.xend.XendRoot import get_compon 2.14 2.15 from xen.xend.uuid import getUuid 2.16 from xen.xend.xenstore.xstransact import xstransact 2.17 -from xen.xend.xenstore.xsutil import IntroduceDomain 2.18 +from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain 2.19 2.20 """Shutdown code for poweroff.""" 2.21 DOMAIN_POWEROFF = 0 2.22 @@ -75,9 +77,6 @@ restart_modes = [ 2.23 RESTART_NEVER, 2.24 ] 2.25 2.26 -STATE_RESTART_PENDING = 'pending' 2.27 -STATE_RESTART_BOOTING = 'booting' 2.28 - 2.29 STATE_VM_OK = "ok" 2.30 STATE_VM_TERMINATED = "terminated" 2.31 STATE_VM_SUSPENDED = "suspended" 2.32 @@ -92,7 +91,29 @@ SIF_NET_BE_DOMAIN = (1<<5) 2.33 SIF_TPM_BE_DOMAIN = (1<<7) 2.34 2.35 2.36 +SHUTDOWN_TIMEOUT = 30 2.37 + 2.38 + 2.39 +DOMROOT = '/domain' 2.40 +VMROOT = '/domain' 2.41 + 2.42 + 2.43 xc = xen.lowlevel.xc.new() 2.44 +xroot = XendRoot.instance() 2.45 + 2.46 + 2.47 +## Configuration entries that we expect to round-trip -- be read from the 2.48 +# config file or xc, written to save-files (i.e. through sxpr), and reused as 2.49 +# config on restart or restore, all without munging. Some configuration 2.50 +# entries are munged for backwards compatibility reasons, or because they 2.51 +# don't come out of xc in the same form as they are specified in the config 2.52 +# file, so those are handled separately. 2.53 +ROUNDTRIPPING_CONFIG_ENTRIES = [ 2.54 + ('name', str), 2.55 + ('ssidref', int), 2.56 + ('cpu_weight', float), 2.57 + ('bootloader', str) 2.58 + ] 2.59 2.60 2.61 def domain_exists(name): 2.62 @@ -145,22 +166,43 @@ class XendDomainInfo: 2.63 2.64 vm = cls(getUuid(), dompath, cls.parseConfig(config)) 2.65 vm.construct() 2.66 + vm.refreshShutdown() 2.67 return vm 2.68 2.69 create = classmethod(create) 2.70 2.71 2.72 - def recreate(cls, uuid, dompath, domid, info): 2.73 - """Create the VM object for an existing domain. 2.74 + def recreate(cls, xeninfo): 2.75 + """Create the VM object for an existing domain.""" 2.76 + 2.77 + log.debug("XendDomainInfo.recreate(%s)", xeninfo) 2.78 2.79 - @param dompath: The path to all domain information 2.80 - @param info: domain info from xc 2.81 - """ 2.82 + domid = xeninfo['dom'] 2.83 + try: 2.84 + dompath = GetDomainPath(domid) 2.85 + if not dompath: 2.86 + raise XendError( 2.87 + 'No domain path in store for existing domain %d' % domid) 2.88 + vmpath = xstransact.Read(dompath, "vm") 2.89 + if not vmpath: 2.90 + raise XendError( 2.91 + 'No vm path in store for existing domain %d' % domid) 2.92 + uuid = xstransact.Read(vmpath, "uuid") 2.93 + if not uuid: 2.94 + raise XendError( 2.95 + 'No vm/uuid path in store for existing domain %d' % domid) 2.96 2.97 - log.debug("XendDomainInfo.recreate(%s, %s, %s, %s)", uuid, dompath, 2.98 - domid, info) 2.99 + dompath = "/".join(dompath.split("/")[0:-1]) 2.100 + except Exception, exn: 2.101 + log.warn(str(exn)) 2.102 + dompath = DOMROOT 2.103 + uuid = getUuid() 2.104 2.105 - return cls(uuid, dompath, info, domid, True) 2.106 + log.info("Recreating domain %d, uuid %s", domid, uuid) 2.107 + 2.108 + vm = cls(uuid, dompath, xeninfo, domid, True) 2.109 + vm.refreshShutdown(xeninfo) 2.110 + return vm 2.111 2.112 recreate = classmethod(recreate) 2.113 2.114 @@ -183,14 +225,12 @@ class XendDomainInfo: 2.115 except TypeError, exn: 2.116 raise VmError('Invalid ssidref in config: %s' % exn) 2.117 2.118 - log.debug('restoring with ssidref = %d' % ssidref) 2.119 - 2.120 vm = cls(uuid, dompath, cls.parseConfig(config), 2.121 xc.domain_create(ssidref = ssidref)) 2.122 - vm.clear_shutdown() 2.123 vm.create_channel() 2.124 vm.configure() 2.125 vm.exportToDB() 2.126 + vm.refreshShutdown() 2.127 return vm 2.128 2.129 restore = classmethod(restore) 2.130 @@ -214,33 +254,28 @@ class XendDomainInfo: 2.131 log.debug("parseConfig: config is %s" % str(config)) 2.132 2.133 result = {} 2.134 - imagecfg = "()" 2.135 2.136 - result['name'] = get_cfg('name') 2.137 - result['ssidref'] = get_cfg('ssidref', int) 2.138 + for e in ROUNDTRIPPING_CONFIG_ENTRIES: 2.139 + result[e[0]] = get_cfg(e[0], e[1]) 2.140 + 2.141 result['memory'] = get_cfg('memory', int) 2.142 result['mem_kb'] = get_cfg('mem_kb', int) 2.143 result['maxmem'] = get_cfg('maxmem', int) 2.144 result['maxmem_kb'] = get_cfg('maxmem_kb', int) 2.145 result['cpu'] = get_cfg('cpu', int) 2.146 - result['cpu_weight'] = get_cfg('cpu_weight', float) 2.147 - result['bootloader'] = get_cfg('bootloader') 2.148 result['restart_mode'] = get_cfg('restart') 2.149 + result['image'] = get_cfg('image') 2.150 2.151 try: 2.152 - imagecfg = get_cfg('image') 2.153 - 2.154 - if imagecfg: 2.155 - result['image'] = imagecfg 2.156 - result['vcpus'] = int(sxp.child_value(imagecfg, 'vcpus', 2.157 - 1)) 2.158 + if result['image']: 2.159 + result['vcpus'] = int(sxp.child_value(result['image'], 2.160 + 'vcpus', 1)) 2.161 else: 2.162 result['vcpus'] = 1 2.163 except TypeError, exn: 2.164 raise VmError( 2.165 'Invalid configuration setting: vcpus = %s: %s' % 2.166 - (sxp.child_value(imagecfg, 'vcpus', 1), 2.167 - str(exn))) 2.168 + (sxp.child_value(result['image'], 'vcpus', 1), str(exn))) 2.169 2.170 result['backend'] = [] 2.171 for c in sxp.children(config, 'backend'): 2.172 @@ -283,26 +318,26 @@ class XendDomainInfo: 2.173 self.store_mfn = None 2.174 self.console_channel = None 2.175 self.console_mfn = None 2.176 - 2.177 - #todo: state: running, suspended 2.178 + 2.179 self.state = STATE_VM_OK 2.180 self.state_updated = threading.Condition() 2.181 - self.shutdown_pending = None 2.182 2.183 - self.restart_state = None 2.184 - self.restart_time = None 2.185 - self.restart_count = 0 2.186 - 2.187 self.writeVm("uuid", self.uuid) 2.188 self.storeDom("vm", self.path) 2.189 2.190 2.191 def augmentInfo(self): 2.192 + """Augment self.info, as given to us through {@link #recreate}, with 2.193 + values taken from the store. This recovers those values known to xend 2.194 + but not to the hypervisor. 2.195 + """ 2.196 def useIfNeeded(name, val): 2.197 if not self.infoIsSet(name) and val is not None: 2.198 self.info[name] = val 2.199 2.200 params = (("name", str), 2.201 + ("restart-mode", str), 2.202 + ("image", str), 2.203 ("start-time", float)) 2.204 2.205 from_store = self.gatherVm(*params) 2.206 @@ -322,13 +357,18 @@ class XendDomainInfo: 2.207 defaultInfo('name', lambda: "Domain-%d" % self.domid) 2.208 defaultInfo('ssidref', lambda: 0) 2.209 defaultInfo('restart_mode', lambda: RESTART_ONREBOOT) 2.210 + defaultInfo('cpu', lambda: None) 2.211 defaultInfo('cpu_weight', lambda: 1.0) 2.212 defaultInfo('bootloader', lambda: None) 2.213 defaultInfo('backend', lambda: []) 2.214 defaultInfo('device', lambda: []) 2.215 + defaultInfo('image', lambda: None) 2.216 2.217 self.check_name(self.info['name']) 2.218 2.219 + if isinstance(self.info['image'], str): 2.220 + self.info['image'] = sxp.from_string(self.info['image']) 2.221 + 2.222 # Internally, we keep only maxmem_KiB, and not maxmem or maxmem_kb 2.223 # (which come from outside, and are in MiB and KiB respectively). 2.224 # This means that any maxmem or maxmem_kb settings here have come 2.225 @@ -451,17 +491,16 @@ class XendDomainInfo: 2.226 'domid': str(self.domid), 2.227 'uuid': self.uuid, 2.228 2.229 - 'restart_time': str(self.restart_time), 2.230 - 2.231 - 'xend/state': self.state, 2.232 - 'xend/restart_count': str(self.restart_count), 2.233 'xend/restart_mode': str(self.info['restart_mode']), 2.234 2.235 'memory/target': str(self.info['memory_KiB']) 2.236 } 2.237 2.238 for (k, v) in self.info.items(): 2.239 - to_store[k] = str(v) 2.240 + if v: 2.241 + to_store[k] = str(v) 2.242 + 2.243 + to_store['image'] = sxp.to_string(self.info['image']) 2.244 2.245 log.debug("Storing %s" % str(to_store)) 2.246 2.247 @@ -513,6 +552,88 @@ class XendDomainInfo: 2.248 self.info['backend'], 0) 2.249 2.250 2.251 + def refreshShutdown(self, xeninfo = None): 2.252 + if xeninfo is None: 2.253 + xeninfo = dom_get(self.domid) 2.254 + if xeninfo is None: 2.255 + # The domain no longer exists. This will occur if we have 2.256 + # scheduled a timer to check for shutdown timeouts and the 2.257 + # shutdown succeeded. 2.258 + return 2.259 + 2.260 + if xeninfo['dying']: 2.261 + # Dying means that a domain has been destroyed, but has not yet 2.262 + # been cleaned up by Xen. This could persist indefinitely if, 2.263 + # for example, another domain has some of its pages mapped. 2.264 + # We might like to diagnose this problem in the future, but for 2.265 + # now all we can sensibly do is ignore it. 2.266 + pass 2.267 + 2.268 + elif xeninfo['crashed']: 2.269 + log.warn('Domain has crashed: name=%s id=%d.', 2.270 + self.info['name'], self.domid) 2.271 + 2.272 + if xroot.get_enable_dump(): 2.273 + self.dumpCore() 2.274 + 2.275 + self.maybeRestart('crashed') 2.276 + 2.277 + elif xeninfo['shutdown']: 2.278 + reason = shutdown_reason(xeninfo['shutdown_reason']) 2.279 + 2.280 + log.info('Domain has shutdown: name=%s id=%d reason=%s.', 2.281 + self.info['name'], self.domid, reason) 2.282 + 2.283 + self.clearRestart() 2.284 + 2.285 + if reason == 'suspend': 2.286 + self.state_set(STATE_VM_SUSPENDED) 2.287 + # Don't destroy the domain. XendCheckpoint will do this once 2.288 + # it has finished. 2.289 + elif reason in ['poweroff', 'reboot']: 2.290 + self.maybeRestart(reason) 2.291 + else: 2.292 + self.destroy() 2.293 + 2.294 + else: 2.295 + # Domain is alive. If we are shutting it down, then check 2.296 + # the timeout on that, and destroy it if necessary. 2.297 + 2.298 + sst = self.readVm('xend/shutdown_start_time') 2.299 + if sst: 2.300 + sst = float(sst) 2.301 + timeout = SHUTDOWN_TIMEOUT - time.time() + sst 2.302 + if timeout < 0: 2.303 + log.info( 2.304 + "Domain shutdown timeout expired: name=%s id=%s", 2.305 + self.info['name'], self.domid) 2.306 + self.destroy() 2.307 + else: 2.308 + log.debug( 2.309 + "Scheduling refreshShutdown on domain %d in %ds.", 2.310 + self.domid, timeout) 2.311 + scheduler.later(timeout, self.refreshShutdown) 2.312 + 2.313 + 2.314 + def shutdown(self, reason): 2.315 + if not reason in shutdown_reasons.values(): 2.316 + raise XendError('invalid reason:' + reason) 2.317 + self.storeVm("control/shutdown", reason) 2.318 + if not reason in ['suspend']: 2.319 + self.storeVm('xend/shutdown_start_time', time.time()) 2.320 + 2.321 + 2.322 + def clearRestart(self): 2.323 + self.removeVm("xend/shutdown_start_time") 2.324 + 2.325 + 2.326 + def maybeRestart(self, reason): 2.327 + if self.restart_needed(reason): 2.328 + self.restart() 2.329 + else: 2.330 + self.destroy() 2.331 + 2.332 + 2.333 def dumpCore(self): 2.334 """Create a core dump for this domain. Nothrow guarantee.""" 2.335 2.336 @@ -526,18 +647,32 @@ class XendDomainInfo: 2.337 self.domid, self.info['name'], str(exn)) 2.338 2.339 2.340 - def closeStoreChannel(self): 2.341 - """Close the store channel, if any. Nothrow guarantee.""" 2.342 + def closeChannel(self, channel, entry): 2.343 + """Close the given channel, if set, and remove the given entry in the 2.344 + store. Nothrow guarantee.""" 2.345 2.346 try: 2.347 - if self.store_channel: 2.348 - try: 2.349 - self.store_channel.close() 2.350 - self.removeDom("store/port") 2.351 - finally: 2.352 - self.store_channel = None 2.353 + try: 2.354 + if channel: 2.355 + channel.close() 2.356 + finally: 2.357 + self.removeDom(entry) 2.358 except Exception, exn: 2.359 log.exception(exn) 2.360 + 2.361 + 2.362 + def closeStoreChannel(self): 2.363 + """Close the store channel, if any. Nothrow guarantee.""" 2.364 + 2.365 + self.closeChannel(self.store_channel, "store/port") 2.366 + self.store_channel = None 2.367 + 2.368 + 2.369 + def closeConsoleChannel(self): 2.370 + """Close the console channel, if any. Nothrow guarantee.""" 2.371 + 2.372 + self.closeChannel(self.console_channel, "console/port") 2.373 + self.console_channel = None 2.374 2.375 2.376 def setConsoleRef(self, ref): 2.377 @@ -566,18 +701,23 @@ class XendDomainInfo: 2.378 2.379 self.info.update(info) 2.380 self.validateInfo() 2.381 + self.refreshShutdown(info) 2.382 2.383 log.debug("XendDomainInfo.update done on domain %d: %s", self.domid, 2.384 self.info) 2.385 2.386 2.387 + ## private: 2.388 + 2.389 def state_set(self, state): 2.390 self.state_updated.acquire() 2.391 if self.state != state: 2.392 self.state = state 2.393 self.state_updated.notifyAll() 2.394 self.state_updated.release() 2.395 - self.exportToDB() 2.396 + 2.397 + 2.398 + ## public: 2.399 2.400 def state_wait(self, state): 2.401 self.state_updated.acquire() 2.402 @@ -585,6 +725,7 @@ class XendDomainInfo: 2.403 self.state_updated.wait() 2.404 self.state_updated.release() 2.405 2.406 + 2.407 def __str__(self): 2.408 s = "<domain" 2.409 s += " id=" + str(self.domid) 2.410 @@ -619,47 +760,49 @@ class XendDomainInfo: 2.411 2.412 def sxpr(self): 2.413 sxpr = ['domain', 2.414 - ['domid', self.domid], 2.415 - ['name', self.info['name']], 2.416 - ['memory', self.info['memory_KiB'] / 1024], 2.417 - ['ssidref', self.info['ssidref']]] 2.418 - if self.uuid: 2.419 - sxpr.append(['uuid', self.uuid]) 2.420 - if self.info: 2.421 - sxpr.append(['maxmem', self.info['maxmem_KiB'] / 1024]) 2.422 + ['domid', self.domid], 2.423 + ['uuid', self.uuid], 2.424 + ['memory', self.info['memory_KiB'] / 1024]] 2.425 + 2.426 + for e in ROUNDTRIPPING_CONFIG_ENTRIES: 2.427 + if self.infoIsSet(e[0]): 2.428 + sxpr.append([e[0], self.info[e[0]]]) 2.429 + 2.430 + sxpr.append(['maxmem', self.info['maxmem_KiB'] / 1024]) 2.431 2.432 - if self.infoIsSet('device'): 2.433 - for (_, c) in self.info['device']: 2.434 - sxpr.append(['device', c]) 2.435 + if self.infoIsSet('image'): 2.436 + sxpr.append(['image', self.info['image']]) 2.437 2.438 - def stateChar(name): 2.439 - if name in self.info: 2.440 - if self.info[name]: 2.441 - return name[0] 2.442 - else: 2.443 - return '-' 2.444 + if self.infoIsSet('device'): 2.445 + for (_, c) in self.info['device']: 2.446 + sxpr.append(['device', c]) 2.447 + 2.448 + def stateChar(name): 2.449 + if name in self.info: 2.450 + if self.info[name]: 2.451 + return name[0] 2.452 else: 2.453 - return '?' 2.454 + return '-' 2.455 + else: 2.456 + return '?' 2.457 2.458 - state = reduce( 2.459 - lambda x, y: x + y, 2.460 - map(stateChar, 2.461 - ['running', 'blocked', 'paused', 'shutdown', 'crashed'])) 2.462 + state = reduce( 2.463 + lambda x, y: x + y, 2.464 + map(stateChar, 2.465 + ['running', 'blocked', 'paused', 'shutdown', 'crashed', 2.466 + 'dying'])) 2.467 2.468 - sxpr.append(['state', state]) 2.469 - if self.infoIsSet('shutdown'): 2.470 - reason = shutdown_reason(self.info['shutdown_reason']) 2.471 - sxpr.append(['shutdown_reason', reason]) 2.472 - if self.infoIsSet('cpu_time'): 2.473 - sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) 2.474 - sxpr.append(['vcpus', self.info['vcpus']]) 2.475 - sxpr.append(['cpumap', self.info['cpumap']]) 2.476 - if self.infoIsSet('vcpu_to_cpu'): 2.477 - sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]]) 2.478 - # build a string, using '|' to separate items, show only up 2.479 - # to number of vcpus in domain, and trim the trailing '|' 2.480 - sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|', 2.481 - self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]]) 2.482 + sxpr.append(['state', state]) 2.483 + if self.infoIsSet('shutdown'): 2.484 + reason = shutdown_reason(self.info['shutdown_reason']) 2.485 + sxpr.append(['shutdown_reason', reason]) 2.486 + if self.infoIsSet('cpu_time'): 2.487 + sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) 2.488 + sxpr.append(['vcpus', self.info['vcpus']]) 2.489 + sxpr.append(['cpumap', self.info['cpumap']]) 2.490 + if self.infoIsSet('vcpu_to_cpu'): 2.491 + sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]]) 2.492 + sxpr.append(['vcpu_to_cpu', self.prettyVCpuMap()]) 2.493 2.494 if self.infoIsSet('start_time'): 2.495 up_time = time.time() - self.info['start_time'] 2.496 @@ -674,14 +817,17 @@ class XendDomainInfo: 2.497 sxpr.append(['console_channel', self.console_channel.sxpr()]) 2.498 if self.console_mfn: 2.499 sxpr.append(['console_mfn', self.console_mfn]) 2.500 - if self.restart_count: 2.501 - sxpr.append(['restart_count', self.restart_count]) 2.502 - if self.restart_state: 2.503 - sxpr.append(['restart_state', self.restart_state]) 2.504 - if self.restart_time: 2.505 - sxpr.append(['restart_time', str(self.restart_time)]) 2.506 + 2.507 return sxpr 2.508 2.509 + 2.510 + ## private: 2.511 + 2.512 + def prettyVCpuMap(self): 2.513 + return '|'.join(map(str, 2.514 + self.info['vcpu_to_cpu'][0:self.info['vcpus']])) 2.515 + 2.516 + 2.517 def check_name(self, name): 2.518 """Check if a vm name is valid. Valid names contain alphabetic characters, 2.519 digits, or characters in '_-.:/+'. 2.520 @@ -719,11 +865,19 @@ class XendDomainInfo: 2.521 @param config: configuration 2.522 @raise: VmError on error 2.523 """ 2.524 - # todo - add support for scheduling params? 2.525 + 2.526 + log.debug('XendDomainInfo.construct: %s %s', 2.527 + str(self.domid), 2.528 + str(self.info['ssidref'])) 2.529 + 2.530 + self.domid = xc.domain_create(dom = 0, ssidref = self.info['ssidref']) 2.531 + 2.532 + if self.domid <= 0: 2.533 + raise VmError('Creating domain failed: name=%s' % 2.534 + self.info['name']) 2.535 + 2.536 try: 2.537 self.initDomain() 2.538 - 2.539 - # Create domain devices. 2.540 self.construct_image() 2.541 self.configure() 2.542 self.exportToDB() 2.543 @@ -737,30 +891,23 @@ class XendDomainInfo: 2.544 2.545 2.546 def initDomain(self): 2.547 - log.debug('XendDomainInfo.initDomain: %s %s %s %s)', 2.548 + log.debug('XendDomainInfo.initDomain: %s %s %s', 2.549 str(self.domid), 2.550 str(self.info['memory_KiB']), 2.551 - str(self.info['ssidref']), 2.552 str(self.info['cpu_weight'])) 2.553 2.554 - self.domid = xc.domain_create(dom = self.domid or 0, 2.555 - ssidref = self.info['ssidref']) 2.556 - 2.557 - if 'image' not in self.info: 2.558 + if not self.infoIsSet('image'): 2.559 raise VmError('Missing image in configuration') 2.560 2.561 self.image = image.create(self, 2.562 self.info['image'], 2.563 self.info['device']) 2.564 2.565 - if self.domid <= 0: 2.566 - raise VmError('Creating domain failed: name=%s' % 2.567 - self.info['name']) 2.568 - 2.569 if self.info['bootloader']: 2.570 self.image.handleBootloading() 2.571 2.572 xc.domain_setcpuweight(self.domid, self.info['cpu_weight']) 2.573 + # XXX Merge with configure_maxmem? 2.574 m = self.image.getDomainMemory(self.info['memory_KiB']) 2.575 xc.domain_setmaxmem(self.domid, m) 2.576 xc.domain_memory_increase_reservation(self.domid, m, 0, 0) 2.577 @@ -794,6 +941,8 @@ class XendDomainInfo: 2.578 self.configure_vcpus(self.info['vcpus']) 2.579 2.580 2.581 + ## public: 2.582 + 2.583 def delete(self): 2.584 """Delete the vm's db. 2.585 """ 2.586 @@ -803,48 +952,36 @@ class XendDomainInfo: 2.587 log.warning("error in domain db delete: %s", ex) 2.588 2.589 2.590 - def destroy_domain(self): 2.591 - """Destroy the vm's domain. 2.592 - The domain will not finally go away unless all vm 2.593 - devices have been released. 2.594 - """ 2.595 - if self.domid is None: 2.596 - return 2.597 - try: 2.598 - xc.domain_destroy(dom=self.domid) 2.599 - except Exception, err: 2.600 - log.exception("Domain destroy failed: %s", self.info['name']) 2.601 + def cleanup(self): 2.602 + """Cleanup vm resources: release devices. Nothrow guarantee.""" 2.603 2.604 - def cleanup(self): 2.605 - """Cleanup vm resources: release devices. 2.606 - """ 2.607 - self.state = STATE_VM_TERMINATED 2.608 + self.state_set(STATE_VM_TERMINATED) 2.609 self.release_devices() 2.610 self.closeStoreChannel() 2.611 - if self.console_channel: 2.612 - # notify processes using this console? 2.613 - try: 2.614 - self.console_channel.close() 2.615 - self.console_channel = None 2.616 - except: 2.617 - pass 2.618 + self.closeConsoleChannel() 2.619 + 2.620 if self.image: 2.621 try: 2.622 self.image.destroy() 2.623 - self.image = None 2.624 except: 2.625 - pass 2.626 + log.exception( 2.627 + "XendDomainInfo.cleanup: image.destroy() failed.") 2.628 + self.image = None 2.629 + 2.630 2.631 def destroy(self): 2.632 - """Cleanup vm and destroy domain. 2.633 - """ 2.634 + """Cleanup vm and destroy domain. Nothrow guarantee.""" 2.635 2.636 - log.debug("XendDomainInfo.destroy") 2.637 + log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid)) 2.638 + 2.639 + self.cleanup() 2.640 2.641 - self.destroy_domain() 2.642 - self.cleanup() 2.643 - self.exportToDB() 2.644 - return 0 2.645 + try: 2.646 + if self.domid is not None: 2.647 + xc.domain_destroy(dom=self.domid) 2.648 + except Exception, exn: 2.649 + log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.") 2.650 + 2.651 2.652 def is_terminated(self): 2.653 """Check if a domain has been terminated. 2.654 @@ -852,8 +989,7 @@ class XendDomainInfo: 2.655 return self.state == STATE_VM_TERMINATED 2.656 2.657 def release_devices(self): 2.658 - """Release all vm devices. 2.659 - """ 2.660 + """Release all vm devices. Nothrow guarantee.""" 2.661 2.662 while True: 2.663 t = xstransact("%s/device" % self.path) 2.664 @@ -865,8 +1001,8 @@ class XendDomainInfo: 2.665 # Log and swallow any exceptions in removal -- 2.666 # there's nothing more we can do. 2.667 log.exception( 2.668 - "Device release failed: %s; %s; %s; %s" % 2.669 - (self.info['name'], n, d, str(ex))) 2.670 + "Device release failed: %s; %s; %s", 2.671 + self.info['name'], n, d) 2.672 if t.commit(): 2.673 break 2.674 2.675 @@ -902,8 +1038,7 @@ class XendDomainInfo: 2.676 2.677 @raise: VmError for invalid devices 2.678 """ 2.679 - if not self.rebooting(): 2.680 - self.create_configured_devices() 2.681 + self.create_configured_devices() 2.682 if self.image: 2.683 self.image.createDeviceModel() 2.684 2.685 @@ -942,23 +1077,6 @@ class XendDomainInfo: 2.686 return reason == 'reboot' 2.687 return False 2.688 2.689 - def restart_cancel(self): 2.690 - """Cancel a vm restart. 2.691 - """ 2.692 - self.restart_state = None 2.693 - 2.694 - def restarting(self): 2.695 - """Put the vm into restart mode. 2.696 - """ 2.697 - self.restart_state = STATE_RESTART_PENDING 2.698 - 2.699 - def restart_pending(self): 2.700 - """Test if the vm has a pending restart. 2.701 - """ 2.702 - return self.restart_state == STATE_RESTART_PENDING 2.703 - 2.704 - def rebooting(self): 2.705 - return self.restart_state == STATE_RESTART_BOOTING 2.706 2.707 def restart_check(self): 2.708 """Check if domain restart is OK. 2.709 @@ -976,23 +1094,37 @@ class XendDomainInfo: 2.710 self.restart_time = tnow 2.711 self.restart_count += 1 2.712 2.713 + 2.714 def restart(self): 2.715 - """Restart the domain after it has exited. 2.716 - Reuses the domain id 2.717 + """Restart the domain after it has exited. """ 2.718 + 2.719 + # self.restart_check() 2.720 + self.cleanup() 2.721 + 2.722 + config = self.sxpr() 2.723 2.724 - """ 2.725 + if self.readVm('xend/restart_in_progress'): 2.726 + log.error('Xend failed during restart of domain %d. ' 2.727 + 'Refusing to restart to avoid loops.', 2.728 + self.domid) 2.729 + self.destroy() 2.730 + return 2.731 + 2.732 + self.writeVm('xend/restart_in_progress', 'True') 2.733 + 2.734 try: 2.735 - self.clear_shutdown() 2.736 - self.state = STATE_VM_OK 2.737 - self.shutdown_pending = None 2.738 - self.restart_check() 2.739 - self.exportToDB() 2.740 - self.restart_state = STATE_RESTART_BOOTING 2.741 - self.configure_bootloader() 2.742 - self.construct() 2.743 - self.exportToDB() 2.744 + self.destroy() 2.745 + try: 2.746 + xd = get_component('xen.xend.XendDomain') 2.747 + xd.domain_unpause(xd.domain_create(config).getDomid()) 2.748 + except Exception, exn: 2.749 + log.exception('Failed to restart domain %d.', self.domid) 2.750 finally: 2.751 - self.restart_state = None 2.752 + self.removeVm('xend/restart_in_progress') 2.753 + 2.754 + # self.configure_bootloader() 2.755 + # self.exportToDB() 2.756 + 2.757 2.758 def configure_bootloader(self): 2.759 if not self.info['bootloader']: 2.760 @@ -1006,7 +1138,8 @@ class XendDomainInfo: 2.761 if dev: 2.762 disk = sxp.child_value(dev, "uname") 2.763 fn = blkdev_uname_to_file(disk) 2.764 - blcfg = bootloader(self.info['bootloader'], fn, 1, self.info['vcpus']) 2.765 + blcfg = bootloader(self.info['bootloader'], fn, 1, 2.766 + self.info['vcpus']) 2.767 if blcfg is None: 2.768 msg = "Had a bootloader specified, but can't find disk" 2.769 log.error(msg) 2.770 @@ -1023,7 +1156,9 @@ class XendDomainInfo: 2.771 2.772 2.773 def configure_maxmem(self): 2.774 - xc.domain_setmaxmem(self.domid, maxmem_kb = self.info['maxmem_KiB']) 2.775 + if self.image: 2.776 + m = self.image.getDomainMemory(self.info['memory_KiB']) 2.777 + xc.domain_setmaxmem(self.domid, maxmem_kb = m) 2.778 2.779 2.780 def vcpu_hotplug(self, vcpu, state): 2.781 @@ -1038,24 +1173,9 @@ class XendDomainInfo: 2.782 availability = "online" 2.783 self.storeVm("cpu/%d/availability" % vcpu, availability) 2.784 2.785 - def shutdown(self, reason): 2.786 - if not reason in shutdown_reasons.values(): 2.787 - raise XendError('invalid reason:' + reason) 2.788 - self.storeVm("control/shutdown", reason) 2.789 - if not reason in ['suspend']: 2.790 - self.shutdown_pending = {'start':time.time(), 'reason':reason} 2.791 - 2.792 - def clear_shutdown(self): 2.793 - self.removeVm("control/shutdown") 2.794 - 2.795 def send_sysrq(self, key=0): 2.796 self.storeVm("control/sysrq", '%c' % key) 2.797 2.798 - def shutdown_time_left(self, timeout): 2.799 - if not self.shutdown_pending: 2.800 - return 0 2.801 - return timeout - (time.time() - self.shutdown_pending['start']) 2.802 - 2.803 def dom0_init_store(self): 2.804 if not self.store_channel: 2.805 self.store_channel = self.eventChannel("store/port") 2.806 @@ -1078,8 +1198,6 @@ class XendDomainInfo: 2.807 def dom0_enforce_vcpus(self): 2.808 dom = 0 2.809 # get max number of vcpus to use for dom0 from config 2.810 - from xen.xend import XendRoot 2.811 - xroot = XendRoot.instance() 2.812 target = int(xroot.get_dom0_vcpus()) 2.813 log.debug("number of vcpus to use is %d" % (target)) 2.814