ia64/xen-unstable

changeset 12710:cefb1f761f0b

Fix HVM shutdown when xend is restarted.

Added a recreate call to ImageHandler, allowing the subclasses of that to
hook into the code that runs when xend restarts. This allows us in particular
to reregister the watches for HVM shutdown, and read the PID of qemu-dm from
the store.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Nov 30 18:08:34 2006 +0000 (2006-11-30)
parents dde9e37c0671
children d7f71de58c4b
files tools/python/xen/xend/XendConstants.py tools/python/xen/xend/XendDomain.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/image.py
line diff
     1.1 --- a/tools/python/xen/xend/XendConstants.py	Thu Nov 30 18:05:19 2006 +0000
     1.2 +++ b/tools/python/xen/xend/XendConstants.py	Thu Nov 30 18:08:34 2006 +0000
     1.3 @@ -34,6 +34,8 @@ DOMAIN_SHUTDOWN_REASONS = {
     1.4      DOMAIN_CRASH   : "crash",
     1.5      DOMAIN_HALT    : "halt"
     1.6  }
     1.7 +REVERSE_DOMAIN_SHUTDOWN_REASONS = \
     1.8 +    dict([(y, x) for x, y in DOMAIN_SHUTDOWN_REASONS.items()])
     1.9  
    1.10  restart_modes = [
    1.11      "restart",
     2.1 --- a/tools/python/xen/xend/XendDomain.py	Thu Nov 30 18:05:19 2006 +0000
     2.2 +++ b/tools/python/xen/xend/XendDomain.py	Thu Nov 30 18:08:34 2006 +0000
     2.3 @@ -421,7 +421,6 @@ class XendDomain:
     2.4                  self._remove_domain(dom, domid)
     2.5  
     2.6  
     2.7 -
     2.8      def _add_domain(self, info):
     2.9          """Add a domain to the list of running domains
    2.10          
     3.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Thu Nov 30 18:05:19 2006 +0000
     3.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Thu Nov 30 18:08:34 2006 +0000
     3.3 @@ -226,6 +226,15 @@ def recreate(info, priv):
     3.4          vm._storeVmDetails()
     3.5          vm._storeDomDetails()
     3.6          
     3.7 +    if vm.info['image']: # Only dom0 should be without an image entry when
     3.8 +                         # recreating, but we cope with missing ones
     3.9 +                         # elsewhere just in case.
    3.10 +        vm.image = image.create(vm,
    3.11 +                                vm.info,
    3.12 +                                vm.info['image'],
    3.13 +                                vm.info['devices'])
    3.14 +        vm.image.recreate()
    3.15 +
    3.16      vm._registerWatches()
    3.17      vm.refreshShutdown(xeninfo)
    3.18      return vm
    3.19 @@ -470,7 +479,7 @@ class XendDomainInfo:
    3.20          
    3.21          if reason not in DOMAIN_SHUTDOWN_REASONS.values():
    3.22              raise XendError('Invalid reason: %s' % reason)
    3.23 -        self._storeDom("control/shutdown", reason)
    3.24 +        self.storeDom("control/shutdown", reason)
    3.25                  
    3.26      def pause(self):
    3.27          """Pause domain
    3.28 @@ -497,7 +506,7 @@ class XendDomainInfo:
    3.29      def send_sysrq(self, key):
    3.30          """ Send a Sysrq equivalent key via xenstored."""
    3.31          asserts.isCharConvertible(key)
    3.32 -        self._storeDom("control/sysrq", '%c' % key)
    3.33 +        self.storeDom("control/sysrq", '%c' % key)
    3.34  
    3.35      def device_create(self, dev_config):
    3.36          """Create a new device.
    3.37 @@ -581,7 +590,7 @@ class XendDomainInfo:
    3.38          
    3.39          self.info['memory_static_min'] = target
    3.40          self.storeVm("memory", target)
    3.41 -        self._storeDom("memory/target", target << 10)
    3.42 +        self.storeDom("memory/target", target << 10)
    3.43  
    3.44      def getVCPUInfo(self):
    3.45          try:
    3.46 @@ -648,7 +657,7 @@ class XendDomainInfo:
    3.47          for devclass in XendDevices.valid_devices():
    3.48              devconfig = self.getDeviceController(devclass).configurations()
    3.49              if devconfig:
    3.50 -                devices.extend(map(lambda conf: (devclass, conf), devconfig))
    3.51 +                devices.extend(devconfig)
    3.52  
    3.53          if not self.info['devices'] and devices is not None:
    3.54              for device in devices:
    3.55 @@ -677,16 +686,19 @@ class XendDomainInfo:
    3.56      # Function to update xenstore /dom/*
    3.57      #
    3.58  
    3.59 -    def _readDom(self, *args):
    3.60 +    def readDom(self, *args):
    3.61          return xstransact.Read(self.dompath, *args)
    3.62  
    3.63 +    def gatherDom(self, *args):
    3.64 +        return xstransact.Gather(self.dompath, *args)
    3.65 +
    3.66      def _writeDom(self, *args):
    3.67          return xstransact.Write(self.dompath, *args)
    3.68  
    3.69      def _removeDom(self, *args):
    3.70          return xstransact.Remove(self.dompath, *args)
    3.71  
    3.72 -    def _storeDom(self, *args):
    3.73 +    def storeDom(self, *args):
    3.74          return xstransact.Store(self.dompath, *args)
    3.75  
    3.76      def _recreateDom(self):
    3.77 @@ -787,17 +799,17 @@ class XendDomainInfo:
    3.78      def _handleShutdownWatch(self, _):
    3.79          log.debug('XendDomainInfo.handleShutdownWatch')
    3.80          
    3.81 -        reason = self._readDom('control/shutdown')
    3.82 +        reason = self.readDom('control/shutdown')
    3.83  
    3.84          if reason and reason != 'suspend':
    3.85 -            sst = self._readDom('xend/shutdown_start_time')
    3.86 +            sst = self.readDom('xend/shutdown_start_time')
    3.87              now = time.time()
    3.88              if sst:
    3.89                  self.shutdownStartTime = float(sst)
    3.90                  timeout = float(sst) + SHUTDOWN_TIMEOUT - now
    3.91              else:
    3.92                  self.shutdownStartTime = now
    3.93 -                self._storeDom('xend/shutdown_start_time', now)
    3.94 +                self.storeDom('xend/shutdown_start_time', now)
    3.95                  timeout = SHUTDOWN_TIMEOUT
    3.96  
    3.97              log.trace(
    3.98 @@ -828,7 +840,7 @@ class XendDomainInfo:
    3.99          return self.dompath
   3.100  
   3.101      def getShutdownReason(self):
   3.102 -        return self._readDom('control/shutdown')
   3.103 +        return self.readDom('control/shutdown')
   3.104  
   3.105      def getStorePort(self):
   3.106          """For use only by image.py and XendCheckpoint.py."""
   3.107 @@ -914,7 +926,7 @@ class XendDomainInfo:
   3.108                  return
   3.109  
   3.110              elif xeninfo['crashed']:
   3.111 -                if self._readDom('xend/shutdown_completed'):
   3.112 +                if self.readDom('xend/shutdown_completed'):
   3.113                      # We've seen this shutdown already, but we are preserving
   3.114                      # the domain for debugging.  Leave it alone.
   3.115                      return
   3.116 @@ -930,7 +942,7 @@ class XendDomainInfo:
   3.117  
   3.118              elif xeninfo['shutdown']:
   3.119                  self._stateSet(DOM_STATE_SHUTDOWN)
   3.120 -                if self._readDom('xend/shutdown_completed'):
   3.121 +                if self.readDom('xend/shutdown_completed'):
   3.122                      # We've seen this shutdown already, but we are preserving
   3.123                      # the domain for debugging.  Leave it alone.
   3.124                      return
   3.125 @@ -1111,7 +1123,7 @@ class XendDomainInfo:
   3.126          log.info("Preserving dead domain %s (%d).", self.info['name_label'],
   3.127                   self.domid)
   3.128          self._unwatchVm()
   3.129 -        self._storeDom('xend/shutdown_completed', 'True')
   3.130 +        self.storeDom('xend/shutdown_completed', 'True')
   3.131          self._stateSet(DOM_STATE_HALTED)
   3.132  
   3.133      #
   3.134 @@ -1724,7 +1736,7 @@ class XendDomainInfo:
   3.135                                     ignore_devices = ignore_store)
   3.136  
   3.137          if not ignore_store and self.dompath:
   3.138 -            vnc_port = self._readDom('console/vnc-port')
   3.139 +            vnc_port = self.readDom('console/vnc-port')
   3.140              if vnc_port is not None:
   3.141                  result.append(['device',
   3.142                                 ['console', ['vnc-port', str(vnc_port)]]])
     4.1 --- a/tools/python/xen/xend/image.py	Thu Nov 30 18:05:19 2006 +0000
     4.2 +++ b/tools/python/xen/xend/image.py	Thu Nov 30 18:08:34 2006 +0000
     4.3 @@ -23,6 +23,7 @@ import math
     4.4  import signal
     4.5  
     4.6  import xen.lowlevel.xc
     4.7 +from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS
     4.8  from xen.xend.XendError import VmError, XendError
     4.9  from xen.xend.XendLogging import log
    4.10  from xen.xend.server.netif import randomMAC
    4.11 @@ -165,6 +166,10 @@ class ImageHandler:
    4.12          pass
    4.13  
    4.14  
    4.15 +    def recreate(self):
    4.16 +        pass
    4.17 +
    4.18 +
    4.19  class LinuxImageHandler(ImageHandler):
    4.20  
    4.21      ostype = "linux"
    4.22 @@ -232,9 +237,12 @@ class PPC_LinuxImageHandler(LinuxImageHa
    4.23  
    4.24  class HVMImageHandler(ImageHandler):
    4.25  
    4.26 +    ostype = "hvm"
    4.27 +
    4.28      def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
    4.29          ImageHandler.__init__(self, vm, vmConfig, imageConfig, deviceConfig)
    4.30          self.shutdownWatch = None
    4.31 +        self.rebootFeatureWatch = None
    4.32  
    4.33      def configure(self, vmConfig, imageConfig, deviceConfig):
    4.34          ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
    4.35 @@ -257,7 +265,7 @@ class HVMImageHandler(ImageHandler):
    4.36                          ("image/device-model", self.device_model),
    4.37                          ("image/display", self.display))
    4.38  
    4.39 -        self.pid = 0
    4.40 +        self.pid = None
    4.41  
    4.42          self.dmargs += self.configVNC(imageConfig)
    4.43  
    4.44 @@ -417,20 +425,30 @@ class HVMImageHandler(ImageHandler):
    4.45          log.info("spawning device models: %s %s", self.device_model, args)
    4.46          # keep track of pid and spawned options to kill it later
    4.47          self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
    4.48 +        self.vm.storeDom("image/device-model-pid", self.pid)
    4.49          log.info("device model pid: %d", self.pid)
    4.50  
    4.51 +    def recreate(self):
    4.52 +        self.register_shutdown_watch()
    4.53 +        self.register_reboot_feature_watch()
    4.54 +        self.pid = self.vm.gatherDom(('image/device-model-pid', int))
    4.55 +
    4.56      def destroy(self):
    4.57          self.unregister_shutdown_watch()
    4.58          self.unregister_reboot_feature_watch();
    4.59 -        if not self.pid:
    4.60 -            return
    4.61 -        try:
    4.62 -            os.kill(self.pid, signal.SIGKILL)
    4.63 -            os.waitpid(self.pid, 0)
    4.64 -        except OSError, e:
    4.65 -            log.warning("Unable to kill device model (pid: %d)" % self.pid)
    4.66 -            
    4.67 -        self.pid = 0
    4.68 +        if self.pid:
    4.69 +            try:
    4.70 +                os.kill(self.pid, signal.SIGKILL)
    4.71 +            except OSError, exn:
    4.72 +                log.exception(exn)
    4.73 +            try:
    4.74 +                os.waitpid(self.pid, 0)
    4.75 +            except OSError, exn:
    4.76 +                # This is expected if Xend has been restarted within the
    4.77 +                # life of this domain.  In this case, we can kill the process,
    4.78 +                # but we can't wait for it because it's not our child.
    4.79 +                pass
    4.80 +            self.pid = None
    4.81  
    4.82      def register_shutdown_watch(self):
    4.83          """ add xen store watch on control/shutdown """
    4.84 @@ -454,23 +472,22 @@ class HVMImageHandler(ImageHandler):
    4.85          """ watch call back on node control/shutdown,
    4.86              if node changed, this function will be called
    4.87          """
    4.88 -        from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
    4.89          xd = xen.xend.XendDomain.instance()
    4.90          try:
    4.91              vm = xd.domain_lookup( self.vm.getDomid() )
    4.92          except XendError:
    4.93              # domain isn't registered, no need to clean it up.
    4.94 -            return
    4.95 +            return False
    4.96  
    4.97          reason = vm.getShutdownReason()
    4.98          log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
    4.99 -        for x in DOMAIN_SHUTDOWN_REASONS.keys():
   4.100 -            if DOMAIN_SHUTDOWN_REASONS[x] == reason:
   4.101 -                vm.info['shutdown'] = 1
   4.102 -                vm.info['shutdown_reason'] = x
   4.103 -                vm.refreshShutdown(vm.info)
   4.104 +        if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS:
   4.105 +            vm.info['shutdown'] = 1
   4.106 +            vm.info['shutdown_reason'] = \
   4.107 +                REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
   4.108 +            vm.refreshShutdown(vm.info)
   4.109  
   4.110 -        return 1 # Keep watching
   4.111 +        return True # Keep watching
   4.112  
   4.113      def register_reboot_feature_watch(self):
   4.114          """ add xen store watch on control/feature-reboot """
   4.115 @@ -494,21 +511,16 @@ class HVMImageHandler(ImageHandler):
   4.116          """ watch call back on node control/feature-reboot,
   4.117              if node changed, this function will be called
   4.118          """
   4.119 -        xd = xen.xend.XendDomain.instance()
   4.120 -        vm = xd.domain_lookup( self.vm.getDomid() )
   4.121 -
   4.122 -        status = vm.readDom('control/feature-reboot')
   4.123 +        status = self.vm.readDom('control/feature-reboot')
   4.124          log.debug("hvm_reboot_feature fired, module status=%s", status)
   4.125          if status == '1':
   4.126              self.unregister_shutdown_watch()
   4.127  
   4.128 -        return 1 # Keep watching
   4.129 +        return True # Keep watching
   4.130  
   4.131  
   4.132  class IA64_HVM_ImageHandler(HVMImageHandler):
   4.133  
   4.134 -    ostype = "hvm"
   4.135 -
   4.136      def getRequiredAvailableMemory(self, mem_kb):
   4.137          page_kb = 16
   4.138          # ROM size for guest firmware, ioreq page and xenstore page
   4.139 @@ -521,8 +533,6 @@ class IA64_HVM_ImageHandler(HVMImageHand
   4.140  
   4.141  class X86_HVM_ImageHandler(HVMImageHandler):
   4.142  
   4.143 -    ostype = "hvm"
   4.144 -
   4.145      def getRequiredAvailableMemory(self, mem_kb):
   4.146          # Add 8 MiB overhead for QEMU's video RAM.
   4.147          return mem_kb + 8192