ia64/xen-unstable

changeset 7072: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>
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 -        @param dompath:   The path to all domain information
    2.78 -        @param info:      domain info from xc
    2.79 -        """
    2.80 +        log.debug("XendDomainInfo.recreate(%s)", xeninfo)
    2.81  
    2.82 -        log.debug("XendDomainInfo.recreate(%s, %s, %s, %s)", uuid, dompath,
    2.83 -                  domid, info)
    2.84 +        domid = xeninfo['dom']
    2.85 +        try:
    2.86 +            dompath = GetDomainPath(domid)
    2.87 +            if not dompath:
    2.88 +                raise XendError(
    2.89 +                    'No domain path in store for existing domain %d' % domid)
    2.90 +            vmpath = xstransact.Read(dompath, "vm")
    2.91 +            if not vmpath:
    2.92 +                raise XendError(
    2.93 +                    'No vm path in store for existing domain %d' % domid)
    2.94 +            uuid = xstransact.Read(vmpath, "uuid")
    2.95 +            if not uuid:
    2.96 +                raise XendError(
    2.97 +                    'No vm/uuid path in store for existing domain %d' % domid)
    2.98  
    2.99 -        return cls(uuid, dompath, info, domid, True)
   2.100 +            dompath = "/".join(dompath.split("/")[0:-1])
   2.101 +        except Exception, exn:
   2.102 +            log.warn(str(exn))
   2.103 +            dompath = DOMROOT
   2.104 +            uuid = getUuid()
   2.105 +
   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 -            if self.infoIsSet('device'):
   2.427 -                for (_, c) in self.info['device']:
   2.428 -                    sxpr.append(['device', c])
   2.429 +        for e in ROUNDTRIPPING_CONFIG_ENTRIES:
   2.430 +            if self.infoIsSet(e[0]):
   2.431 +                sxpr.append([e[0], self.info[e[0]]])
   2.432 +        
   2.433 +        sxpr.append(['maxmem', self.info['maxmem_KiB'] / 1024])
   2.434  
   2.435 -            def stateChar(name):
   2.436 -                if name in self.info:
   2.437 -                    if self.info[name]:
   2.438 -                        return name[0]
   2.439 -                    else:
   2.440 -                        return '-'
   2.441 +        if self.infoIsSet('image'):
   2.442 +            sxpr.append(['image', self.info['image']])
   2.443 +
   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 -
   2.455 -            state = reduce(
   2.456 -                lambda x, y: x + y,
   2.457 -                map(stateChar,
   2.458 -                    ['running', 'blocked', 'paused', 'shutdown', 'crashed']))
   2.459 +                    return '-'
   2.460 +            else:
   2.461 +                return '?'
   2.462  
   2.463 -            sxpr.append(['state', state])
   2.464 -            if self.infoIsSet('shutdown'):
   2.465 -                reason = shutdown_reason(self.info['shutdown_reason'])
   2.466 -                sxpr.append(['shutdown_reason', reason])
   2.467 -            if self.infoIsSet('cpu_time'):
   2.468 -                sxpr.append(['cpu_time', self.info['cpu_time']/1e9])    
   2.469 -            sxpr.append(['vcpus', self.info['vcpus']])
   2.470 -            sxpr.append(['cpumap', self.info['cpumap']])
   2.471 -            if self.infoIsSet('vcpu_to_cpu'):
   2.472 -                sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
   2.473 -                # build a string, using '|' to separate items, show only up
   2.474 -                # to number of vcpus in domain, and trim the trailing '|'
   2.475 -                sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
   2.476 -                            self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
   2.477 +        state = reduce(
   2.478 +            lambda x, y: x + y,
   2.479 +            map(stateChar,
   2.480 +                ['running', 'blocked', 'paused', 'shutdown', 'crashed',
   2.481 +                 'dying']))
   2.482 +
   2.483 +        sxpr.append(['state', state])
   2.484 +        if self.infoIsSet('shutdown'):
   2.485 +            reason = shutdown_reason(self.info['shutdown_reason'])
   2.486 +            sxpr.append(['shutdown_reason', reason])
   2.487 +        if self.infoIsSet('cpu_time'):
   2.488 +            sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
   2.489 +        sxpr.append(['vcpus', self.info['vcpus']])
   2.490 +        sxpr.append(['cpumap', self.info['cpumap']])
   2.491 +        if self.infoIsSet('vcpu_to_cpu'):
   2.492 +            sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
   2.493 +            sxpr.append(['vcpu_to_cpu', self.prettyVCpuMap()])
   2.494              
   2.495          if self.infoIsSet('start_time'):
   2.496              up_time =  time.time() - self.info['start_time']
   2.497 @@ -674,14 +817,17 @@ class XendDomainInfo:
   2.498              sxpr.append(['console_channel', self.console_channel.sxpr()])
   2.499          if self.console_mfn:
   2.500              sxpr.append(['console_mfn', self.console_mfn])
   2.501 -        if self.restart_count:
   2.502 -            sxpr.append(['restart_count', self.restart_count])
   2.503 -        if self.restart_state:
   2.504 -            sxpr.append(['restart_state', self.restart_state])
   2.505 -        if self.restart_time:
   2.506 -            sxpr.append(['restart_time', str(self.restart_time)])
   2.507 +
   2.508          return sxpr
   2.509  
   2.510 +
   2.511 +    ## private:
   2.512 +
   2.513 +    def prettyVCpuMap(self):
   2.514 +        return '|'.join(map(str,
   2.515 +                            self.info['vcpu_to_cpu'][0:self.info['vcpus']]))
   2.516 +
   2.517 +
   2.518      def check_name(self, name):
   2.519          """Check if a vm name is valid. Valid names contain alphabetic characters,
   2.520          digits, or characters in '_-.:/+'.
   2.521 @@ -719,11 +865,19 @@ class XendDomainInfo:
   2.522          @param config: configuration
   2.523          @raise: VmError on error
   2.524          """
   2.525 -        # todo - add support for scheduling params?
   2.526 +
   2.527 +        log.debug('XendDomainInfo.construct: %s %s',
   2.528 +                  str(self.domid),
   2.529 +                  str(self.info['ssidref']))
   2.530 +
   2.531 +        self.domid = xc.domain_create(dom = 0, ssidref = self.info['ssidref'])
   2.532 +
   2.533 +        if self.domid <= 0:
   2.534 +            raise VmError('Creating domain failed: name=%s' %
   2.535 +                          self.info['name'])
   2.536 +
   2.537          try:
   2.538              self.initDomain()
   2.539 -
   2.540 -            # Create domain devices.
   2.541              self.construct_image()
   2.542              self.configure()
   2.543              self.exportToDB()
   2.544 @@ -737,30 +891,23 @@ class XendDomainInfo:
   2.545  
   2.546  
   2.547      def initDomain(self):
   2.548 -        log.debug('XendDomainInfo.initDomain: %s %s %s %s)',
   2.549 +        log.debug('XendDomainInfo.initDomain: %s %s %s',
   2.550                    str(self.domid),
   2.551                    str(self.info['memory_KiB']),
   2.552 -                  str(self.info['ssidref']),
   2.553                    str(self.info['cpu_weight']))
   2.554  
   2.555 -        self.domid = xc.domain_create(dom = self.domid or 0,
   2.556 -                                      ssidref = self.info['ssidref'])
   2.557 -
   2.558 -        if 'image' not in self.info:
   2.559 +        if not self.infoIsSet('image'):
   2.560              raise VmError('Missing image in configuration')
   2.561  
   2.562          self.image = image.create(self,
   2.563                                    self.info['image'],
   2.564                                    self.info['device'])
   2.565  
   2.566 -        if self.domid <= 0:
   2.567 -            raise VmError('Creating domain failed: name=%s' %
   2.568 -                          self.info['name'])
   2.569 -
   2.570          if self.info['bootloader']:
   2.571              self.image.handleBootloading()
   2.572  
   2.573          xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
   2.574 +        # XXX Merge with configure_maxmem?
   2.575          m = self.image.getDomainMemory(self.info['memory_KiB'])
   2.576          xc.domain_setmaxmem(self.domid, m)
   2.577          xc.domain_memory_increase_reservation(self.domid, m, 0, 0)
   2.578 @@ -794,6 +941,8 @@ class XendDomainInfo:
   2.579          self.configure_vcpus(self.info['vcpus'])
   2.580  
   2.581  
   2.582 +    ## public:
   2.583 +
   2.584      def delete(self):
   2.585          """Delete the vm's db.
   2.586          """
   2.587 @@ -803,48 +952,36 @@ class XendDomainInfo:
   2.588              log.warning("error in domain db delete: %s", ex)
   2.589  
   2.590  
   2.591 -    def destroy_domain(self):
   2.592 -        """Destroy the vm's domain.
   2.593 -        The domain will not finally go away unless all vm
   2.594 -        devices have been released.
   2.595 -        """
   2.596 -        if self.domid is None:
   2.597 -            return
   2.598 -        try:
   2.599 -            xc.domain_destroy(dom=self.domid)
   2.600 -        except Exception, err:
   2.601 -            log.exception("Domain destroy failed: %s", self.info['name'])
   2.602 +    def cleanup(self):
   2.603 +        """Cleanup vm resources: release devices.  Nothrow guarantee."""
   2.604  
   2.605 -    def cleanup(self):
   2.606 -        """Cleanup vm resources: release devices.
   2.607 -        """
   2.608 -        self.state = STATE_VM_TERMINATED
   2.609 +        self.state_set(STATE_VM_TERMINATED)
   2.610          self.release_devices()
   2.611          self.closeStoreChannel()
   2.612 -        if self.console_channel:
   2.613 -            # notify processes using this console?
   2.614 -            try:
   2.615 -                self.console_channel.close()
   2.616 -                self.console_channel = None
   2.617 -            except:
   2.618 -                pass
   2.619 +        self.closeConsoleChannel()
   2.620 +
   2.621          if self.image:
   2.622              try:
   2.623                  self.image.destroy()
   2.624 -                self.image = None
   2.625              except:
   2.626 -                pass
   2.627 +                log.exception(
   2.628 +                    "XendDomainInfo.cleanup: image.destroy() failed.")
   2.629 +            self.image = None
   2.630 +
   2.631  
   2.632      def destroy(self):
   2.633 -        """Cleanup vm and destroy domain.
   2.634 -        """
   2.635 +        """Cleanup vm and destroy domain.  Nothrow guarantee."""
   2.636  
   2.637 -        log.debug("XendDomainInfo.destroy")
   2.638 +        log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
   2.639  
   2.640 -        self.destroy_domain()
   2.641          self.cleanup()
   2.642 -        self.exportToDB()
   2.643 -        return 0
   2.644 +
   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 -        """
   2.720 +        #            self.restart_check()
   2.721 +        self.cleanup()
   2.722 +
   2.723 +        config = self.sxpr()
   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