Merge branch 'stable-2.9' into stable-2.10
[ganeti-local] / lib / hypervisor / hv_xen.py
index b0c4c72..bcd21a7 100644 (file)
@@ -167,6 +167,15 @@ def _GetInstanceList(fn, include_node, _timeout=5):
   return _ParseInstanceList(lines, include_node)
 
 
+def _IsInstanceRunning(instance_info):
+  return instance_info == "r-----" \
+      or instance_info == "-b----"
+
+
+def _IsInstanceShutdown(instance_info):
+  return instance_info == "---s--"
+
+
 def _ParseNodeInfo(info):
   """Return information about the node.
 
@@ -287,7 +296,7 @@ def _GetConfigFileDiskData(block_devices, blockdev_prefix,
 
   disk_data = []
 
-  for sd_suffix, (cfdev, dev_path) in zip(_letters, block_devices):
+  for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
     sd_name = blockdev_prefix + sd_suffix
 
     if cfdev.mode == constants.DISK_RDWR:
@@ -296,7 +305,7 @@ def _GetConfigFileDiskData(block_devices, blockdev_prefix,
       mode = "r"
 
     if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
-      driver = _FILE_DRIVER_MAP[cfdev.physical_id[0]]
+      driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
     else:
       driver = "phy"
 
@@ -305,6 +314,19 @@ def _GetConfigFileDiskData(block_devices, blockdev_prefix,
   return disk_data
 
 
+def _QuoteCpuidField(data):
+  """Add quotes around the CPUID field only if necessary.
+
+  Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
+  them, and lists of XEND strings, which don't.
+
+  @param data: Either type of parameter.
+  @return: The quoted version thereof.
+
+  """
+  return "'%s'" % data if data.startswith("host") else data
+
+
 class XenHypervisor(hv_base.BaseHypervisor):
   """Xen generic hypervisor interface
 
@@ -343,6 +365,19 @@ class XenHypervisor(hv_base.BaseHypervisor):
 
     self._cmd = _cmd
 
+  @staticmethod
+  def _GetCommandFromHvparams(hvparams):
+    """Returns the Xen command extracted from the given hvparams.
+
+    @type hvparams: dict of strings
+    @param hvparams: hypervisor parameters
+
+    """
+    if hvparams is None or constants.HV_XEN_CMD not in hvparams:
+      raise errors.HypervisorError("Cannot determine xen command.")
+    else:
+      return hvparams[constants.HV_XEN_CMD]
+
   def _GetCommand(self, hvparams):
     """Returns Xen command to use.
 
@@ -351,10 +386,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
 
     """
     if self._cmd is None:
-      if hvparams is None or constants.HV_XEN_CMD not in hvparams:
-        raise errors.HypervisorError("Cannot determine xen command.")
-      else:
-        cmd = hvparams[constants.HV_XEN_CMD]
+      cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
     else:
       cmd = self._cmd
 
@@ -388,12 +420,13 @@ class XenHypervisor(hv_base.BaseHypervisor):
     return utils.PathJoin(self._cfgdir, instance_name)
 
   @classmethod
-  def _WriteNICInfoFile(cls, instance_name, idx, nic):
+  def _WriteNICInfoFile(cls, instance, idx, nic):
     """Write the Xen config file for the instance.
 
     This version of the function just writes the config file from static data.
 
     """
+    instance_name = instance.name
     dirs = [(dname, constants.RUN_DIRS_MODE)
             for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
     utils.EnsureDirs(dirs)
@@ -401,24 +434,19 @@ class XenHypervisor(hv_base.BaseHypervisor):
     cfg_file = cls._InstanceNICFile(instance_name, idx)
     data = StringIO()
 
+    data.write("TAGS=%s\n" % "\ ".join(instance.GetTags()))
     if nic.netinfo:
       netinfo = objects.Network.FromDict(nic.netinfo)
-      data.write("NETWORK_NAME=%s\n" % netinfo.name)
-      if netinfo.network:
-        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
-      if netinfo.gateway:
-        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
-      if netinfo.network6:
-        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
-      if netinfo.gateway6:
-        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
-      if netinfo.mac_prefix:
-        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
-      if netinfo.tags:
-        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
+      for k, v in netinfo.HooksDict().iteritems():
+        data.write("%s=%s\n" % (k, v))
 
     data.write("MAC=%s\n" % nic.mac)
-    data.write("IP=%s\n" % nic.ip)
+    if nic.ip:
+      data.write("IP=%s\n" % nic.ip)
+    data.write("INTERFACE_INDEX=%s\n" % str(idx))
+    if nic.name:
+      data.write("INTERFACE_NAME=%s\n" % nic.name)
+    data.write("INTERFACE_UUID=%s\n" % nic.uuid)
     data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
     data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
 
@@ -594,24 +622,65 @@ class XenHypervisor(hv_base.BaseHypervisor):
 
     return self._StopInstance(name, force, instance.hvparams)
 
+  def _ShutdownInstance(self, name, hvparams):
+    """Shutdown an instance if the instance is running.
+
+    @type name: string
+    @param name: name of the instance to stop
+    @type hvparams: dict of string
+    @param hvparams: hypervisor parameters of the instance
+
+    The '-w' flag waits for shutdown to complete which avoids the need
+    to poll in the case where we want to destroy the domain
+    immediately after shutdown.
+
+    """
+    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
+
+    if instance_info is None or _IsInstanceShutdown(instance_info[4]):
+      logging.info("Failed to shutdown instance %s, not running", name)
+      return None
+
+    return self._RunXen(["shutdown", "-w", name], hvparams)
+
+  def _DestroyInstance(self, name, hvparams):
+    """Destroy an instance if the instance if the instance exists.
+
+    @type name: string
+    @param name: name of the instance to destroy
+    @type hvparams: dict of string
+    @param hvparams: hypervisor parameters of the instance
+
+    """
+    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
+
+    if instance_info is None:
+      logging.info("Failed to destroy instance %s, does not exist", name)
+      return None
+
+    return self._RunXen(["destroy", name], hvparams)
+
   def _StopInstance(self, name, force, hvparams):
     """Stop an instance.
 
     @type name: string
-    @param name: name of the instance to be shutdown
+    @param name: name of the instance to destroy
+
     @type force: boolean
-    @param force: flag specifying whether shutdown should be forced
+    @param force: whether to do a "hard" stop (destroy)
+
     @type hvparams: dict of string
     @param hvparams: hypervisor parameters of the instance
 
     """
     if force:
-      action = "destroy"
+      result = self._DestroyInstance(name, hvparams)
     else:
-      action = "shutdown"
+      self._ShutdownInstance(name, hvparams)
+      result = self._DestroyInstance(name, hvparams)
 
-    result = self._RunXen([action, name], hvparams)
-    if result.failed:
+    if result is not None and result.failed and \
+          self.GetInstanceInfo(name, hvparams=hvparams) is not None:
       raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
                                    (name, result.fail_reason, result.output))
 
@@ -692,11 +761,12 @@ class XenHypervisor(hv_base.BaseHypervisor):
     instance_list = self._GetInstanceList(True, hvparams)
     return _GetNodeInfo(result.stdout, instance_list)
 
-  def GetInstanceConsole(self, instance, primary_node, hvparams, beparams):
+  @classmethod
+  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
     """Return a command for connecting to the console of an instance.
 
     """
-    xen_cmd = self._GetCommand(hvparams)
+    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
     return objects.InstanceConsole(instance=instance.name,
                                    kind=constants.CONS_SSH,
                                    host=primary_node.name,
@@ -960,6 +1030,8 @@ class XenPvmHypervisor(XenHypervisor):
     constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
     constants.HV_XEN_CMD:
       hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
+    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
+    constants.HV_SOUNDHW: hv_base.NO_CHECK,
     }
 
   def _GetConfig(self, instance, startup_memory, block_devices):
@@ -1025,7 +1097,7 @@ class XenPvmHypervisor(XenHypervisor):
       if hvp[constants.HV_VIF_SCRIPT]:
         nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
       vif_data.append("'%s'" % nic_str)
-      self._WriteNICInfoFile(instance.name, idx, nic)
+      self._WriteNICInfoFile(instance, idx, nic)
 
     disk_data = \
       _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
@@ -1043,6 +1115,13 @@ class XenPvmHypervisor(XenHypervisor):
     config.write("on_crash = 'restart'\n")
     config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
 
+    cpuid = hvp[constants.HV_XEN_CPUID]
+    if cpuid:
+      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
+
+    if hvp[constants.HV_SOUNDHW]:
+      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
+
     return config.getvalue()
 
 
@@ -1093,6 +1172,8 @@ class XenHvmHypervisor(XenHypervisor):
     constants.HV_VIRIDIAN: hv_base.NO_CHECK,
     constants.HV_XEN_CMD:
       hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
+    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
+    constants.HV_SOUNDHW: hv_base.NO_CHECK,
     }
 
   def _GetConfig(self, instance, startup_memory, block_devices):
@@ -1196,7 +1277,7 @@ class XenHvmHypervisor(XenHypervisor):
       if hvp[constants.HV_VIF_SCRIPT]:
         nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
       vif_data.append("'%s'" % nic_str)
-      self._WriteNICInfoFile(instance.name, idx, nic)
+      self._WriteNICInfoFile(instance, idx, nic)
 
     config.write("vif = [%s]\n" % ",".join(vif_data))
 
@@ -1222,4 +1303,11 @@ class XenHvmHypervisor(XenHypervisor):
       config.write("on_reboot = 'destroy'\n")
     config.write("on_crash = 'restart'\n")
 
+    cpuid = hvp[constants.HV_XEN_CPUID]
+    if cpuid:
+      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
+
+    if hvp[constants.HV_SOUNDHW]:
+      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
+
     return config.getvalue()