commit 938adc87ae8ba2a306073637b7764e0d6165c587
Author: Constantinos Venetsanopoulos <cven@grnet.gr>
Date:   Wed Mar 28 12:29:22 2012 +0300

    Multiple ExtStorage Providers and ext-params
    
    Add support for passing parameters to the ext template (ext-params).
    Take advantage of disk-params, that don't seem to make much sense in
    this template (ExtStorage Providers are not predefined and we don't
    know their needs) and use them to pass the ext-params dynamically to
    the template.
    
    ext-params are correlated with gnt-os-interface's os-params.
    All ext-params are exported to the ExtStorage Provider through it's
    environment, with variables prefixed with 'EXTP_' (similarly to the
    OS interface's 'OSP_' params).
    
    ext-params are passed via the --disk option. If the disk template
    is of type `ext', then any additional options passed to --disk and
    are not in IDISK_PARAMS are considered ext-params e.g.:
    
     gnt-instance add -t ext --disk=0:size=2G,param1=value1,param2=value2
    
    Finally, we introduce a new IDISK_PARAM called IDISK_PROVIDER, that is
    mandatory for template `ext' and is used to select the desired
    ExtStorage Provider. This parameter is not a valid --disk option for
    any other template type.
    
    The IDISK_PROVIDER parameter becomes the first element of the
    disk's unique_id tuple e.g.:
    
     unique_id = ('sample_provider1', 'UUID.ext.diskX')
    
    Example selecting different ExtStorage Providers for each disk and
    passing different ext-params to them:
    
     -t ext --disk=0:size=2G,provider=sample_provider1,param1=value1
            --disk=1:size=3G,provider=sample_provider2,param2=value2
    
    Signed-off-by: Constantinos Venetsanopoulos <cven@grnet.gr>
    Signed-off-by: Iustin Pop <iustin@google.com>
    [iustin@google.com: small simplification in bdev code, pylint fixes]
    Reviewed-by: Iustin Pop <iustin@google.com>

diff --git a/lib/bdev.py b/lib/bdev.py
index cff63a3..c452e57 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -2802,6 +2802,7 @@ class ExtStorageDevice(BlockDev):
       raise ValueError("Invalid configuration data %s" % str(unique_id))
 
     self.driver, self.vol_name = unique_id
+    self.ext_params = params
 
     self.major = self.minor = None
     self.Attach()
@@ -2820,7 +2821,8 @@ class ExtStorageDevice(BlockDev):
 
     # Call the External Storage's create script,
     # to provision a new Volume inside the External Storage
-    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id, str(size))
+    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
+                      params, str(size))
 
     return ExtStorageDevice(unique_id, children, size, params)
 
@@ -2837,7 +2839,8 @@ class ExtStorageDevice(BlockDev):
 
     # Call the External Storage's remove script,
     # to remove the Volume from the External Storage
-    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id)
+    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
+                      self.ext_params)
 
   def Rename(self, new_id):
     """Rename this device.
@@ -2857,7 +2860,7 @@ class ExtStorageDevice(BlockDev):
     # Call the External Storage's attach script,
     # to attach an existing Volume to a block device under /dev
     self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
-                                      self.unique_id)
+                                      self.unique_id, self.ext_params)
 
     try:
       st = os.stat(self.dev_path)
@@ -2891,7 +2894,8 @@ class ExtStorageDevice(BlockDev):
 
     # Call the External Storage's detach script,
     # to detach an existing Volume from it's block device under /dev
-    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id)
+    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
+                      self.ext_params)
 
     self.minor = None
     self.dev_path = None
@@ -2932,7 +2936,7 @@ class ExtStorageDevice(BlockDev):
     # Call the External Storage's grow script,
     # to grow an existing Volume inside the External Storage
     _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
-                      str(self.size), grow=str(new_size))
+                      self.ext_params, str(self.size), grow=str(new_size))
 
   def SetInfo(self, text):
     """Update metadata with info text.
@@ -2948,10 +2952,11 @@ class ExtStorageDevice(BlockDev):
     # Call the External Storage's setinfo script,
     # to set metadata for an existing Volume inside the External Storage
     _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
-                      metadata=text)
+                      self.ext_params, metadata=text)
 
 
-def _ExtStorageAction(action, unique_id, size=None, grow=None, metadata=None):
+def _ExtStorageAction(action, unique_id, ext_params,
+                      size=None, grow=None, metadata=None):
   """Take an External Storage action.
 
   Take an External Storage action concerning or affecting
@@ -2963,6 +2968,8 @@ def _ExtStorageAction(action, unique_id, size=None, grow=None, metadata=None):
   @type unique_id: tuple (driver, vol_name)
   @param unique_id: a tuple containing the type of ExtStorage (driver)
                     and the Volume name
+  @type ext_params: dict
+  @param ext_params: ExtStorage parameters
   @type size: integer
   @param size: the size of the Volume in mebibytes
   @type grow: integer
@@ -2980,7 +2987,8 @@ def _ExtStorageAction(action, unique_id, size=None, grow=None, metadata=None):
     _ThrowError("%s" % inst_es)
 
   # Create the basic environment for the driver's scripts
-  create_env = _ExtStorageEnvironment(unique_id, size, grow, metadata)
+  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
+                                      grow, metadata)
 
   # Do not use log file for action `attach' as we need
   # to get the output from RunResult
@@ -3053,7 +3061,9 @@ def ExtStorageFromDisk(name, base_dir=None):
   # an optional one
   es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
 
-  for filename in es_files:
+  es_files[constants.ES_PARAMETERS_FILE] = True
+
+  for (filename, _) in es_files.items():
     es_files[filename] = utils.PathJoin(es_dir, filename)
 
     try:
@@ -3071,6 +3081,16 @@ def ExtStorageFromDisk(name, base_dir=None):
         return False, ("File '%s' under path '%s' is not executable" %
                        (filename, es_dir))
 
+  parameters = []
+  if constants.ES_PARAMETERS_FILE in es_files:
+    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
+    try:
+      parameters = utils.ReadFile(parameters_file).splitlines()
+    except EnvironmentError, err:
+      return False, ("Error while reading the EXT parameters file at %s: %s" %
+                     (parameters_file, utils.ErrnoOrStr(err)))
+    parameters = [v.split(None, 1) for v in parameters]
+
   es_obj = \
     objects.ExtStorage(name=name, path=es_dir,
                        create_script=es_files[constants.ES_SCRIPT_CREATE],
@@ -3078,15 +3098,20 @@ def ExtStorageFromDisk(name, base_dir=None):
                        grow_script=es_files[constants.ES_SCRIPT_GROW],
                        attach_script=es_files[constants.ES_SCRIPT_ATTACH],
                        detach_script=es_files[constants.ES_SCRIPT_DETACH],
-                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO])
+                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
+                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
+                       supported_parameters=parameters)
   return True, es_obj
 
 
-def _ExtStorageEnvironment(unique_id, size=None, grow=None, metadata=None):
+def _ExtStorageEnvironment(unique_id, ext_params,
+                           size=None, grow=None, metadata=None):
   """Calculate the environment for an External Storage script.
 
   @type unique_id: tuple (driver, vol_name)
   @param unique_id: ExtStorage pool and name of the Volume
+  @type ext_params: dict
+  @param ext_params: the EXT parameters
   @type size: string
   @param size: size of the Volume (in mebibytes)
   @type grow: string
@@ -3102,6 +3127,10 @@ def _ExtStorageEnvironment(unique_id, size=None, grow=None, metadata=None):
   result = {}
   result["VOL_NAME"] = vol_name
 
+  # EXT params
+  for pname, pvalue in ext_params.items():
+    result["EXTP_%s" % pname.upper()] = str(pvalue)
+
   if size is not None:
     result["VOL_SIZE"] = size
 
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 07c722f..071cbd4 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -7302,6 +7302,7 @@ class LUInstanceRecreateDisks(LogicalUnit):
     # TODO: Implement support changing VG while recreating
     constants.IDISK_VG,
     constants.IDISK_METAVG,
+    constants.IDISK_PROVIDER,
     ]))
 
   def _RunAllocator(self):
@@ -9192,13 +9193,26 @@ def _GenerateDiskTemplate(
     elif template_name == constants.DT_RBD:
       logical_id_fn = lambda idx, _, disk: ("rbd", names[idx])
     elif template_name == constants.DT_EXT:
-      logical_id_fn = lambda idx, _, disk: ("ext", names[idx])
+      def logical_id_fn(idx, _, disk):
+        provider = disk.get(constants.IDISK_PROVIDER, None)
+        if provider is None:
+          raise errors.ProgrammerError("Disk template is %s, but '%s' is"
+                                       " not found", constants.DT_EXT,
+                                       constants.IDISK_PROVIDER)
+        return (provider, names[idx])
     else:
       raise errors.ProgrammerError("Unknown disk template '%s'" % template_name)
 
     dev_type = _DISK_TEMPLATE_DEVICE_TYPE[template_name]
 
     for idx, disk in enumerate(disk_info):
+      params = {}
+      # Only for the Ext template add disk_info to params
+      if template_name == constants.DT_EXT:
+        params[constants.IDISK_PROVIDER] = disk[constants.IDISK_PROVIDER]
+        for key in disk:
+          if key not in constants.IDISK_PARAMS:
+            params[key] = disk[key]
       disk_index = idx + base_index
       size = disk[constants.IDISK_SIZE]
       feedback_fn("* disk %s, size %s" %
@@ -9207,7 +9221,7 @@ def _GenerateDiskTemplate(
                                 logical_id=logical_id_fn(idx, disk_index, disk),
                                 iv_name="disk/%d" % disk_index,
                                 mode=disk[constants.IDISK_MODE],
-                                params={}))
+                                params=params))
 
   return disks
 
@@ -9657,7 +9671,7 @@ def _ComputeDisks(op, default_vg):
   @param op: The instance opcode
   @param default_vg: The default_vg to assume
 
-  @return: The computer disks
+  @return: The computed disks
 
   """
   disks = []
@@ -9675,16 +9689,37 @@ def _ComputeDisks(op, default_vg):
       raise errors.OpPrereqError("Invalid disk size '%s'" % size,
                                  errors.ECODE_INVAL)
 
+    ext_provider = disk.get(constants.IDISK_PROVIDER, None)
+    if ext_provider and op.disk_template != constants.DT_EXT:
+      raise errors.OpPrereqError("The '%s' option is only valid for the %s"
+                                 " disk template, not %s" %
+                                 (constants.IDISK_PROVIDER, constants.DT_EXT,
+                                 op.disk_template), errors.ECODE_INVAL)
+
     data_vg = disk.get(constants.IDISK_VG, default_vg)
     new_disk = {
       constants.IDISK_SIZE: size,
       constants.IDISK_MODE: mode,
       constants.IDISK_VG: data_vg,
       }
+
     if constants.IDISK_METAVG in disk:
       new_disk[constants.IDISK_METAVG] = disk[constants.IDISK_METAVG]
     if constants.IDISK_ADOPT in disk:
       new_disk[constants.IDISK_ADOPT] = disk[constants.IDISK_ADOPT]
+
+    # For extstorage, demand the `provider' option and add any
+    # additional parameters (ext-params) to the dict
+    if op.disk_template == constants.DT_EXT:
+      if ext_provider:
+        new_disk[constants.IDISK_PROVIDER] = ext_provider
+        for key in disk:
+          if key not in constants.IDISK_PARAMS:
+            new_disk[key] = disk[key]
+      else:
+        raise errors.OpPrereqError("Missing provider for template '%s'" %
+                                   constants.DT_EXT, errors.ECODE_INVAL)
+
     disks.append(new_disk)
 
   return disks
@@ -9751,7 +9786,8 @@ class LUInstanceCreate(LogicalUnit):
     # check disks. parameter names and consistent adopt/no-adopt strategy
     has_adopt = has_no_adopt = False
     for disk in self.op.disks:
-      utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
+      if self.op.disk_template != constants.DT_EXT:
+        utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
       if constants.IDISK_ADOPT in disk:
         has_adopt = True
       else:
@@ -10544,6 +10580,9 @@ class LUInstanceCreate(LogicalUnit):
 
     _CheckNicsBridgesExist(self, self.nics, self.pnode.name)
 
+    #TODO: _CheckExtParams (remotely)
+    # Check parameters for extstorage
+
     # memory check on primary node
     #TODO(dynmem): use MINMEM for checking
     if self.op.start:
diff --git a/lib/constants.py b/lib/constants.py
index 263e318..900b4bc 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -676,6 +676,7 @@ ES_ACTION_GROW = "grow"
 ES_ACTION_ATTACH = "attach"
 ES_ACTION_DETACH = "detach"
 ES_ACTION_SETINFO = "setinfo"
+ES_ACTION_VERIFY = "verify"
 
 ES_SCRIPT_CREATE = ES_ACTION_CREATE
 ES_SCRIPT_REMOVE = ES_ACTION_REMOVE
@@ -683,15 +684,19 @@ ES_SCRIPT_GROW = ES_ACTION_GROW
 ES_SCRIPT_ATTACH = ES_ACTION_ATTACH
 ES_SCRIPT_DETACH = ES_ACTION_DETACH
 ES_SCRIPT_SETINFO = ES_ACTION_SETINFO
+ES_SCRIPT_VERIFY = ES_ACTION_VERIFY
 ES_SCRIPTS = frozenset([
   ES_SCRIPT_CREATE,
   ES_SCRIPT_REMOVE,
   ES_SCRIPT_GROW,
   ES_SCRIPT_ATTACH,
   ES_SCRIPT_DETACH,
-  ES_SCRIPT_SETINFO
+  ES_SCRIPT_SETINFO,
+  ES_SCRIPT_VERIFY
   ])
 
+ES_PARAMETERS_FILE = "parameters.list"
+
 # ssh constants
 SSH = "ssh"
 SCP = "scp"
@@ -1175,12 +1180,14 @@ IDISK_MODE = "mode"
 IDISK_ADOPT = "adopt"
 IDISK_VG = "vg"
 IDISK_METAVG = "metavg"
+IDISK_PROVIDER = "provider"
 IDISK_PARAMS_TYPES = {
   IDISK_SIZE: VTYPE_SIZE,
   IDISK_MODE: VTYPE_STRING,
   IDISK_ADOPT: VTYPE_STRING,
   IDISK_VG: VTYPE_STRING,
   IDISK_METAVG: VTYPE_STRING,
+  IDISK_PROVIDER: VTYPE_STRING,
   }
 IDISK_PARAMS = frozenset(IDISK_PARAMS_TYPES.keys())
 
diff --git a/lib/objects.py b/lib/objects.py
index 3638d7a..e49aaad 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -909,6 +909,9 @@ class Disk(ConfigObject):
         constants.LDP_POOL: dt_params[constants.RBD_POOL],
         }))
 
+    elif disk_template == constants.DT_EXT:
+      result.append(constants.DISK_LD_DEFAULTS[constants.LD_EXT])
+
     return result
 
 
@@ -1249,6 +1252,8 @@ class ExtStorage(ConfigObject):
     "attach_script",
     "detach_script",
     "setinfo_script",
+    "verify_script",
+    "supported_parameters",
     ]
 
 
