Merge branch 'stable-2.2' into devel-2.2
[ganeti-local] / lib / cmdlib.py
index f5886a2..baded30 100644 (file)
@@ -154,6 +154,13 @@ def _TDict(val):
   return isinstance(val, dict)
 
 
+def _TIsLength(size):
+  """Check is the given container is of the given size.
+
+  """
+  return lambda container: len(container) == size
+
+
 # Combinator types
 def _TAnd(*args):
   """Combine multiple functions using an AND operation.
@@ -173,6 +180,13 @@ def _TOr(*args):
   return fn
 
 
+def _TMap(fn, test):
+  """Checks that a modified version of the argument passes the given test.
+
+  """
+  return lambda val: test(fn(val))
+
+
 # Type aliases
 
 #: a non-empty string
@@ -236,6 +250,9 @@ _PNodeName = ("node_name", _NoDefault, _TNonEmptyString)
 _PMigrationMode = ("mode", None, _TOr(_TNone,
                                       _TElemOf(constants.HT_MIGRATION_MODES)))
 
+#: the obsolete 'live' mode (boolean)
+_PMigrationLive = ("live", None, _TMaybeBool)
+
 
 # End types
 class LogicalUnit(object):
@@ -1085,9 +1102,8 @@ def _CheckOSVariant(os_obj, name):
   """
   if not os_obj.supported_variants:
     return
-  try:
-    variant = name.split("+", 1)[1]
-  except IndexError:
+  variant = objects.OS.GetVariant(name)
+  if not variant:
     raise errors.OpPrereqError("OS name must include a variant",
                                errors.ECODE_INVAL)
 
@@ -2583,6 +2599,8 @@ class LURenameCluster(LogicalUnit):
         self.LogWarning("Could not re-enable the master role on"
                         " the master, please restart manually: %s", msg)
 
+    return clustername
+
 
 class LUSetClusterParams(LogicalUnit):
   """Change the parameters of the cluster.
@@ -2606,6 +2624,17 @@ class LUSetClusterParams(LogicalUnit):
     ("nicparams", None, _TOr(_TDict, _TNone)),
     ("drbd_helper", None, _TOr(_TString, _TNone)),
     ("default_iallocator", None, _TMaybeString),
+    ("reserved_lvs", None, _TOr(_TListOf(_TNonEmptyString), _TNone)),
+    ("hidden_oss", None, _TOr(_TListOf(\
+          _TAnd(_TList,
+                _TIsLength(2),
+                _TMap(lambda v: v[0], _TElemOf(constants.DDMS_VALUES)))),
+          _TNone)),
+    ("blacklisted_oss", None, _TOr(_TListOf(\
+          _TAnd(_TList,
+                _TIsLength(2),
+                _TMap(lambda v: v[0], _TElemOf(constants.DDMS_VALUES)))),
+          _TNone)),
     ]
   REQ_BGL = False
 
@@ -2876,6 +2905,33 @@ class LUSetClusterParams(LogicalUnit):
     if self.op.default_iallocator is not None:
       self.cluster.default_iallocator = self.op.default_iallocator
 
+    if self.op.reserved_lvs is not None:
+      self.cluster.reserved_lvs = self.op.reserved_lvs
+
+    def helper_oss(aname, mods, desc):
+      lst = getattr(self.cluster, aname)
+      for key, val in mods:
+        if key == constants.DDM_ADD:
+          if val in lst:
+            feedback_fn("OS %s already in %s, ignoring", val, desc)
+          else:
+            lst.append(val)
+        elif key == constants.DDM_REMOVE:
+          if val in lst:
+            lst.remove(val)
+          else:
+            feedback_fn("OS %s not found in %s, ignoring", val, desc)
+        else:
+          raise errors.ProgrammerError("Invalid modification '%s'" % key)
+
+    if self.op.hidden_oss:
+      helper_oss("hidden_oss", self.op.hidden_oss,
+                 "hidden OS list")
+
+    if self.op.blacklisted_oss:
+      helper_oss("blacklisted_oss", self.op.blacklisted_oss,
+                 "blacklisted OS list")
+
     self.cfg.Update(self.cluster, feedback_fn)
 
 
@@ -3064,9 +3120,12 @@ class LUDiagnoseOS(NoHooksLU):
     ("names", _EmptyList, _TListOf(_TNonEmptyString)),
     ]
   REQ_BGL = False
+  _HID = "hidden"
+  _BLK = "blacklisted"
+  _VLD = "valid"
   _FIELDS_STATIC = utils.FieldSet()
-  _FIELDS_DYNAMIC = utils.FieldSet("name", "valid", "node_status", "variants",
-                                   "parameters", "api_versions")
+  _FIELDS_DYNAMIC = utils.FieldSet("name", _VLD, "node_status", "variants",
+                                   "parameters", "api_versions", _HID, _BLK)
 
   def CheckArguments(self):
     if self.op.names:
@@ -3133,8 +3192,10 @@ class LUDiagnoseOS(NoHooksLU):
     node_data = self.rpc.call_os_diagnose(valid_nodes)
     pol = self._DiagnoseByOS(node_data)
     output = []
+    cluster = self.cfg.GetClusterInfo()
 
-    for os_name, os_data in pol.items():
+    for os_name in utils.NiceSort(pol.keys()):
+      os_data = pol[os_name]
       row = []
       valid = True
       (variants, params, api_versions) = null_state = (set(), set(), set())
@@ -3153,10 +3214,17 @@ class LUDiagnoseOS(NoHooksLU):
           params.intersection_update(node_params)
           api_versions.intersection_update(node_api)
 
+      is_hid = os_name in cluster.hidden_oss
+      is_blk = os_name in cluster.blacklisted_oss
+      if ((self._HID not in self.op.output_fields and is_hid) or
+          (self._BLK not in self.op.output_fields and is_blk) or
+          (self._VLD not in self.op.output_fields and not valid)):
+        continue
+
       for field in self.op.output_fields:
         if field == "name":
           val = os_name
-        elif field == "valid":
+        elif field == self._VLD:
           val = valid
         elif field == "node_status":
           # this is just a copy of the dict
@@ -3164,11 +3232,15 @@ class LUDiagnoseOS(NoHooksLU):
           for node_name, nos_list in os_data.items():
             val[node_name] = nos_list
         elif field == "variants":
-          val = list(variants)
+          val = utils.NiceSort(list(variants))
         elif field == "parameters":
           val = list(params)
         elif field == "api_versions":
           val = list(api_versions)
+        elif field == self._HID:
+          val = is_hid
+        elif field == self._BLK:
+          val = is_blk
         else:
           raise errors.ParameterError(field)
         row.append(val)
@@ -3959,7 +4031,7 @@ class LUSetNodeParams(LogicalUnit):
       # we can't change the master's node flags
       if self.op.node_name == self.cfg.GetMasterNode():
         raise errors.OpPrereqError("The master role can be changed"
-                                   " only via masterfailover",
+                                   " only via master-failover",
                                    errors.ECODE_INVAL)
 
 
@@ -4866,10 +4938,19 @@ class LURenameInstance(LogicalUnit):
   _OP_PARAMS = [
     _PInstanceName,
     ("new_name", _NoDefault, _TNonEmptyString),
-    ("ignore_ip", False, _TBool),
-    ("check_name", True, _TBool),
+    ("ip_check", False, _TBool),
+    ("name_check", True, _TBool),
     ]
 
+  def CheckArguments(self):
+    """Check arguments.
+
+    """
+    if self.op.ip_check and not self.op.name_check:
+      # TODO: make the ip check more flexible and not depend on the name check
+      raise errors.OpPrereqError("Cannot do ip check without a name check",
+                                 errors.ECODE_INVAL)
+
   def BuildHooksEnv(self):
     """Build hooks env.
 
@@ -4895,23 +4976,21 @@ class LURenameInstance(LogicalUnit):
     _CheckInstanceDown(self, instance, "cannot rename")
     self.instance = instance
 
-    # new name verification
-    if self.op.check_name:
-      name_info = netutils.GetHostInfo(self.op.new_name)
-      self.op.new_name = name_info.name
-
     new_name = self.op.new_name
+    if self.op.name_check:
+      hostinfo = netutils.HostInfo(netutils.HostInfo.NormalizeName(new_name))
+      new_name = self.op.new_name = hostinfo.name
+      if (self.op.ip_check and
+          netutils.TcpPing(hostinfo.ip, constants.DEFAULT_NODED_PORT)):
+        raise errors.OpPrereqError("IP %s of instance %s already in use" %
+                                   (hostinfo.ip, new_name),
+                                   errors.ECODE_NOTUNIQUE)
 
     instance_list = self.cfg.GetInstanceList()
     if new_name in instance_list:
       raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
                                  new_name, errors.ECODE_EXISTS)
 
-    if not self.op.ignore_ip:
-      if netutils.TcpPing(name_info.ip, constants.DEFAULT_NODED_PORT):
-        raise errors.OpPrereqError("IP %s of instance %s already in use" %
-                                   (name_info.ip, new_name),
-                                   errors.ECODE_NOTUNIQUE)
 
   def Exec(self, feedback_fn):
     """Reinstall the instance.
@@ -4954,6 +5033,8 @@ class LURenameInstance(LogicalUnit):
     finally:
       _ShutdownInstanceDisks(self, inst)
 
+    return inst.name
+
 
 class LURemoveInstance(LogicalUnit):
   """Remove an instance.
@@ -5497,6 +5578,7 @@ class LUMigrateInstance(LogicalUnit):
   _OP_PARAMS = [
     _PInstanceName,
     _PMigrationMode,
+    _PMigrationLive,
     ("cleanup", False, _TBool),
     ]
 
@@ -5728,6 +5810,7 @@ class LUMigrateNode(LogicalUnit):
   _OP_PARAMS = [
     _PNodeName,
     _PMigrationMode,
+    _PMigrationLive,
     ]
   REQ_BGL = False
 
@@ -5832,7 +5915,19 @@ class TLMigrateInstance(Tasklet):
 
     self.instance = instance
 
-    if self.lu.op.mode is None:
+    if self.lu.op.live is not None and self.lu.op.mode is not None:
+      raise errors.OpPrereqError("Only one of the 'live' and 'mode'"
+                                 " parameters are accepted",
+                                 errors.ECODE_INVAL)
+    if self.lu.op.live is not None:
+      if self.lu.op.live:
+        self.lu.op.mode = constants.HT_MIGRATION_LIVE
+      else:
+        self.lu.op.mode = constants.HT_MIGRATION_NONLIVE
+      # reset the 'live' parameter to None so that repeated
+      # invocations of CheckPrereq do not raise an exception
+      self.lu.op.live = None
+    elif self.lu.op.mode is None:
       # read the default value from the hypervisor
       i_hv = self.cfg.GetClusterInfo().FillHV(instance, skip_globals=False)
       self.lu.op.mode = i_hv[constants.HV_MIGRATION_MODE]
@@ -6492,7 +6587,7 @@ class LUCreateInstance(LogicalUnit):
     ("os_type", None, _TMaybeString),
     ("force_variant", False, _TBool),
     ("source_handshake", None, _TOr(_TList, _TNone)),
-    ("source_x509_ca", None, _TOr(_TList, _TNone)),
+    ("source_x509_ca", None, _TMaybeString),
     ("source_instance_name", None, _TMaybeString),
     ("src_node", None, _TMaybeString),
     ("src_path", None, _TMaybeString),
@@ -6504,7 +6599,6 @@ class LUCreateInstance(LogicalUnit):
     ("identify_defaults", False, _TBool),
     ("file_driver", None, _TOr(_TNone, _TElemOf(constants.FILE_DRIVER))),
     ("file_storage_dir", None, _TMaybeString),
-    ("dry_run", False, _TBool),
     ]
   REQ_BGL = False
 
@@ -6523,7 +6617,7 @@ class LUCreateInstance(LogicalUnit):
 
     if self.op.ip_check and not self.op.name_check:
       # TODO: make the ip check more flexible and not depend on the name check
-      raise errors.OpPrereqError("Cannot do ip checks without a name check",
+      raise errors.OpPrereqError("Cannot do ip check without a name check",
                                  errors.ECODE_INVAL)
 
     # check nics' parameter names
@@ -6581,6 +6675,16 @@ class LUCreateInstance(LogicalUnit):
     ### Node/iallocator related checks
     _CheckIAllocatorOrNode(self, "iallocator", "pnode")
 
+    if self.op.pnode is not None:
+      if self.op.disk_template in constants.DTS_NET_MIRROR:
+        if self.op.snode is None:
+          raise errors.OpPrereqError("The networked disk templates need"
+                                     " a mirror node", errors.ECODE_INVAL)
+      elif self.op.snode:
+        self.LogWarning("Secondary node will be ignored on non-mirrored disk"
+                        " template")
+        self.op.snode = None
+
     self._cds = _GetClusterDomainSecret()
 
     if self.op.mode == constants.INSTANCE_IMPORT:
@@ -6596,6 +6700,10 @@ class LUCreateInstance(LogicalUnit):
       if self.op.os_type is None:
         raise errors.OpPrereqError("No guest OS specified",
                                    errors.ECODE_INVAL)
+      if self.op.os_type in self.cfg.GetClusterInfo().blacklisted_oss:
+        raise errors.OpPrereqError("Guest OS '%s' is not allowed for"
+                                   " installation" % self.op.os_type,
+                                   errors.ECODE_STATE)
       if self.op.disk_template is None:
         raise errors.OpPrereqError("No disk template specified",
                                    errors.ECODE_INVAL)
@@ -7128,9 +7236,6 @@ class LUCreateInstance(LogicalUnit):
 
     # mirror node verification
     if self.op.disk_template in constants.DTS_NET_MIRROR:
-      if self.op.snode is None:
-        raise errors.OpPrereqError("The networked disk templates need"
-                                   " a mirror node", errors.ECODE_INVAL)
       if self.op.snode == pnode.name:
         raise errors.OpPrereqError("The secondary node cannot be the"
                                    " primary node.", errors.ECODE_INVAL)
@@ -9701,7 +9806,8 @@ class LUGetTags(TagsLU):
   """
   _OP_PARAMS = [
     ("kind", _NoDefault, _TElemOf(constants.VALID_TAG_TYPES)),
-    ("name", _NoDefault, _TNonEmptyString),
+    # Name is only meaningful for nodes and instances
+    ("name", _NoDefault, _TMaybeString),
     ]
   REQ_BGL = False
 
@@ -9760,7 +9866,8 @@ class LUAddTags(TagsLU):
   """
   _OP_PARAMS = [
     ("kind", _NoDefault, _TElemOf(constants.VALID_TAG_TYPES)),
-    ("name", _NoDefault, _TNonEmptyString),
+    # Name is only meaningful for nodes and instances
+    ("name", _NoDefault, _TMaybeString),
     ("tags", _NoDefault, _TListOf(_TNonEmptyString)),
     ]
   REQ_BGL = False
@@ -9793,7 +9900,8 @@ class LUDelTags(TagsLU):
   """
   _OP_PARAMS = [
     ("kind", _NoDefault, _TElemOf(constants.VALID_TAG_TYPES)),
-    ("name", _NoDefault, _TNonEmptyString),
+    # Name is only meaningful for nodes and instances
+    ("name", _NoDefault, _TMaybeString),
     ("tags", _NoDefault, _TListOf(_TNonEmptyString)),
     ]
   REQ_BGL = False
@@ -10008,6 +10116,7 @@ class LUTestJobqueue(NoHooksLU):
     self.LogInfo("Executing")
 
     if self.op.log_messages:
+      self._Notify(False, constants.JQT_STARTMSG, len(self.op.log_messages))
       for idx, msg in enumerate(self.op.log_messages):
         self.LogInfo("Sending log message %s", idx + 1)
         feedback_fn(constants.JQT_MSGPREFIX + msg)