(2.9) Make NIC setup more flexible in case of Xen
authorDimitris Aragiorgis <dimara@grnet.gr>
Wed, 26 Jun 2013 14:38:15 +0000 (17:38 +0300)
committerDimitris Aragiorgis <dimara@grnet.gr>
Thu, 27 Mar 2014 07:56:40 +0000 (09:56 +0200)
For every NIC write down a file that contains NIC's
info (MAC, mode, link, network details), that can
be sourced by any vif script.  The file location is:
/var/run/ganeti/xen-hypervisor/nic/<domname>/<nicidx>.

This file is created upon cfg file creation and before
starting the instance and can be sourced by an external
script. Before file creation ensure that all parent dirs
exist.

Cleanup NIC dir when removing config files. Upon Xen
configuration file removal, remove NIC dir as well.

Add new hv param vif_script that allows execution of
user defined networking script and overrides the one
xend is configured with in xend-config.sxp

Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>
Signed-off-by: Thomas Thrainer <thomasth@google.com>
Reviewed-by: Thomas Thrainer <thomasth@google.com>

lib/constants.py
lib/hypervisor/hv_xen.py

index 1453104..2966efe 100644 (file)
@@ -954,6 +954,7 @@ HV_KVM_PATH = "kvm_path"
 HV_VIF_TYPE = "vif_type"
 HV_VNET_HDR = "vnet_hdr"
 HV_VIRIDIAN = "viridian"
+HV_VIF_SCRIPT = "vif_script"
 
 
 HVS_PARAMETER_TYPES = {
@@ -1026,6 +1027,7 @@ HVS_PARAMETER_TYPES = {
   HV_VIF_TYPE: VTYPE_STRING,
   HV_VNET_HDR: VTYPE_BOOL,
   HV_VIRIDIAN: VTYPE_BOOL,
+  HV_VIF_SCRIPT: VTYPE_STRING,
   }
 
 HVS_PARAMETERS = frozenset(HVS_PARAMETER_TYPES.keys())
@@ -2036,6 +2038,7 @@ HVC_DEFAULTS = {
     HV_CPU_MASK: CPU_PINNING_ALL,
     HV_CPU_CAP: 0,
     HV_CPU_WEIGHT: 256,
+    HV_VIF_SCRIPT: "",
     },
   HT_XEN_HVM: {
     HV_BOOT_ORDER: "cd",
@@ -2059,6 +2062,7 @@ HVC_DEFAULTS = {
     HV_CPU_WEIGHT: 256,
     HV_VIF_TYPE: HT_HVM_VIF_IOEMU,
     HV_VIRIDIAN: False,
+    HV_VIF_SCRIPT: "",
     },
   HT_KVM: {
     HV_KVM_PATH: KVM_PATH,
index 90e9ecb..3ff0c0f 100644 (file)
@@ -24,7 +24,9 @@
 """
 
 import logging
+import errno
 import string # pylint: disable=W0402
+import shutil
 from cStringIO import StringIO
 
 from ganeti import constants
@@ -320,6 +322,9 @@ class XenHypervisor(hv_base.BaseHypervisor):
   CAN_MIGRATE = True
   REBOOT_RETRY_COUNT = 60
   REBOOT_RETRY_INTERVAL = 10
+  _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
+  _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
+  _DIRS = [_ROOT_DIR, _NICS_DIR]
 
   ANCILLARY_FILES = [
     XEND_CONFIG_FILE,
@@ -391,6 +396,61 @@ class XenHypervisor(hv_base.BaseHypervisor):
     return utils.PathJoin(self._cfgdir, instance_name)
 
   @classmethod
+  def _WriteNICInfoFile(cls, instance_name, idx, nic):
+    """Write the Xen config file for the instance.
+
+    This version of the function just writes the config file from static data.
+
+    """
+    dirs = [(dname, constants.RUN_DIRS_MODE)
+            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
+    utils.EnsureDirs(dirs)
+
+    cfg_file = cls._InstanceNICFile(instance_name, idx)
+    data = StringIO()
+
+    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" % "\ ".join(netinfo.tags))
+
+    data.write("MAC=%s\n" % nic.mac)
+    data.write("IP=%s\n" % nic.ip)
+    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
+    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
+
+    try:
+      utils.WriteFile(cfg_file, data=data.getvalue())
+    except EnvironmentError, err:
+      raise errors.HypervisorError("Cannot write Xen instance configuration"
+                                   " file %s: %s" % (cfg_file, err))
+
+  @classmethod
+  def _InstanceNICDir(cls, instance_name):
+    """Returns the directory holding the tap device files for a given instance.
+
+    """
+    return utils.PathJoin(cls._NICS_DIR, instance_name)
+
+  @classmethod
+  def _InstanceNICFile(cls, instance_name, seq):
+    """Returns the name of the file containing the tap device for a given NIC
+
+    """
+    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
+
+  @classmethod
   def _GetConfig(cls, instance, startup_memory, block_devices):
     """Build Xen configuration for an instance.
 
@@ -431,6 +491,11 @@ class XenHypervisor(hv_base.BaseHypervisor):
 
     """
     utils.RemoveFile(self._ConfigFileName(instance_name))
+    try:
+      shutil.rmtree(self._InstanceNICDir(instance_name))
+    except OSError, err:
+      if err.errno != errno.ENOENT:
+        raise
 
   def _StashConfigFile(self, instance_name):
     """Move the Xen config file to the log directory and return its new path.
@@ -875,6 +940,7 @@ class XenPvmHypervisor(XenHypervisor):
     constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
     constants.HV_CPU_WEIGHT:
       (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
+    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
     }
 
   def _GetConfig(self, instance, startup_memory, block_devices):
@@ -926,14 +992,17 @@ class XenPvmHypervisor(XenHypervisor):
     config.write("name = '%s'\n" % instance.name)
 
     vif_data = []
-    for nic in instance.nics:
+    for idx, nic in enumerate(instance.nics):
       nic_str = "mac=%s" % (nic.mac)
       ip = getattr(nic, "ip", None)
       if ip is not None:
         nic_str += ", ip=%s" % ip
       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
+      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)
 
     disk_data = \
       _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
@@ -998,6 +1067,7 @@ class XenHvmHypervisor(XenHypervisor):
     constants.HV_VIF_TYPE:
       hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
     constants.HV_VIRIDIAN: hv_base.NO_CHECK,
+    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
     }
 
   def _GetConfig(self, instance, startup_memory, block_devices):
@@ -1091,14 +1161,17 @@ class XenHvmHypervisor(XenHypervisor):
       # parameter 'model' is only valid with type 'ioemu'
       nic_type_str = ", model=%s, type=%s" % \
         (nic_type, constants.HT_HVM_VIF_IOEMU)
-    for nic in instance.nics:
+    for idx, nic in enumerate(instance.nics):
       nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
       ip = getattr(nic, "ip", None)
       if ip is not None:
         nic_str += ", ip=%s" % ip
       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
+      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)
 
     config.write("vif = [%s]\n" % ",".join(vif_data))