From: Apollon Oikonomopoulos <[email protected]>

Modify LUFailoverInstance to enable shared storage instances to failover.
Shared storage instance failover requires either a target node or an
iallocator to determine the target node.

The hook environment variables {OLD,NEW}_SECONDARY will be blank for shared
storage instances.

Signed-off-by: Apollon Oikonomopoulos <[email protected]>
---
 lib/cmdlib.py |  103 +++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 82 insertions(+), 21 deletions(-)

diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 55ff7bf..97e18ad 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -5024,9 +5024,15 @@ class LUFailoverInstance(LogicalUnit):
     """
     self.shutdown_timeout = getattr(self.op, "shutdown_timeout",
                                     constants.DEFAULT_SHUTDOWN_TIMEOUT)
+    self.iallocator = getattr(self.op, "iallocator", None)
+    self.target_node = getattr(self.op, "target_node", None)
 
   def ExpandNames(self):
     self._ExpandAndLockInstance()
+
+    if self.target_node is not None:
+      self.target_node = _ExpandNodeName(self.cfg, self.op.target_node)
+
     self.needed_locks[locking.LEVEL_NODE] = []
     self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
 
@@ -5042,15 +5048,19 @@ class LUFailoverInstance(LogicalUnit):
     """
     instance = self.instance
     source_node = instance.primary_node
-    target_node = instance.secondary_nodes[0]
     env = {
       "IGNORE_CONSISTENCY": self.op.ignore_consistency,
       "SHUTDOWN_TIMEOUT": self.shutdown_timeout,
       "OLD_PRIMARY": source_node,
-      "OLD_SECONDARY": target_node,
-      "NEW_PRIMARY": target_node,
-      "NEW_SECONDARY": source_node,
+      "NEW_PRIMARY": self.target_node,
       }
+
+    if instance.disk_template in constants.DTS_NET_MIRROR:
+      env["OLD_SECONDARY"] = instance.secondary_nodes[0]
+      env["NEW_SECONDARY"] = source_node
+    else:
+      env["OLD_SECONDARY"] = env["NEW_SECONDARY"] = None
+
     env.update(_BuildInstanceHookEnvByObject(self, instance))
     nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes)
     nl_post = list(nl)
@@ -5068,19 +5078,39 @@ class LUFailoverInstance(LogicalUnit):
       "Cannot retrieve locked instance %s" % self.op.instance_name
 
     bep = self.cfg.GetClusterInfo().FillBE(instance)
-    if instance.disk_template not in constants.DTS_NET_MIRROR:
+    if instance.disk_template not in constants.DTS_MIRRORED:
       raise errors.OpPrereqError("Instance's disk layout is not"
-                                 " network mirrored, cannot failover.",
+                                 " mirrored, cannot failover.",
                                  errors.ECODE_STATE)
 
-    secondary_nodes = instance.secondary_nodes
-    if not secondary_nodes:
-      raise errors.ProgrammerError("no secondary node but using "
-                                   "a mirrored disk template")
+    if instance.disk_template in constants.DTS_EXT_MIRROR:
+      if [self.iallocator, self.target_node].count(None) != 1:
+        raise errors.OpPrereqError("Please specify either a target node using"
+                                   " -n or an iallocator using -I for disk"
+                                   " template %s" % instance.disk_template,
+                                   errors.ECODE_INVAL)
+
+      if self.iallocator:
+        self._RunAllocator()
+
+      # self.target_node is already populated, either directly or by the
+      # iallocator run
+      target_node = self.target_node
+
+    else:
+      secondary_nodes = instance.secondary_nodes
+      if not secondary_nodes:
+        raise errors.ConfigurationError("No secondary node but using"
+                                        " %s disk template" %
+                                        instance.disk_template)
+      target_node = secondary_nodes[0]
 
-    target_node = secondary_nodes[0]
     _CheckNodeOnline(self, target_node)
     _CheckNodeNotDrained(self, target_node)
+
+    # Save target_node so that we can use it in BuildHooksEnv
+    self.target_node = target_node
+
     if instance.admin_up:
       # check memory requirements on the secondary node
       _CheckNodeFreeMemory(self, target_node, "failing over instance %s" %
@@ -5103,7 +5133,7 @@ class LUFailoverInstance(LogicalUnit):
     instance = self.instance
 
     source_node = instance.primary_node
-    target_node = instance.secondary_nodes[0]
+    target_node = self.target_node
 
     if instance.admin_up:
       feedback_fn("* checking disk consistency between source and target")
@@ -5134,9 +5164,10 @@ class LUFailoverInstance(LogicalUnit):
                                  " node %s: %s" %
                                  (instance.name, source_node, msg))
 
-    feedback_fn("* deactivating the instance's disks on source node")
-    if not _ShutdownInstanceDisks(self, instance, ignore_primary=True):
-      raise errors.OpExecError("Can't shut down the instance's disks.")
+    if instance.disk_template in constants.DTS_NET_MIRROR:
+      feedback_fn("* deactivating the instance's disks on source node")
+      if not _ShutdownInstanceDisks(self, instance, ignore_primary=True):
+        raise errors.OpExecError("Can't shut down the instance's disks.")
 
     instance.primary_node = target_node
     # distribute new instance config to the other nodes
@@ -5148,20 +5179,50 @@ class LUFailoverInstance(LogicalUnit):
       logging.info("Starting instance %s on node %s",
                    instance.name, target_node)
 
-      disks_ok, _ = _AssembleInstanceDisks(self, instance,
-                                               ignore_secondaries=True)
-      if not disks_ok:
-        _ShutdownInstanceDisks(self, instance)
-        raise errors.OpExecError("Can't activate the instance's disks")
+      if instance.disk_template in constants.DTS_NET_MIRROR:
+        disks_ok, _ = _AssembleInstanceDisks(self, instance,
+                                                 ignore_secondaries=True)
+        if not disks_ok:
+          _ShutdownInstanceDisks(self, instance)
+          raise errors.OpExecError("Can't activate the instance's disks")
 
       feedback_fn("* starting the instance on the target node")
       result = self.rpc.call_instance_start(target_node, instance, None, None)
       msg = result.fail_msg
-      if msg:
+      if msg and instance.disk_template in constants.DTS_NET_MIRROR:
         _ShutdownInstanceDisks(self, instance)
         raise errors.OpExecError("Could not start instance %s on node %s: %s" %
                                  (instance.name, target_node, msg))
 
+  def _RunAllocator(self):
+    """Run the allocator based on input opcode.
+
+    """
+    ial = IAllocator(self.cfg, self.rpc,
+                     mode=constants.IALLOCATOR_MODE_RELOC,
+                     name=self.instance.name,
+                     # TODO See why hail breaks with a single node below
+                     relocate_from=[self.instance.primary_node,
+                                    self.instance.primary_node],
+                     )
+
+    ial.Run(self.iallocator)
+
+    if not ial.success:
+      raise errors.OpPrereqError("Can't compute nodes using"
+                                 " iallocator '%s': %s" %
+                                 (self.iallocator, ial.info),
+                                 errors.ECODE_NORES)
+    if len(ial.result) != ial.required_nodes:
+      raise errors.OpPrereqError("iallocator '%s' returned invalid number"
+                                 " of nodes (%s), required %s" %
+                                 (self.iallocator, len(ial.result),
+                                  ial.required_nodes), errors.ECODE_FAULT)
+    self.target_node = ial.result[0]
+    self.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
+                 self.instance.name, self.iallocator,
+                 utils.CommaJoin(ial.result))
+
 
 class LUMigrateInstance(LogicalUnit):
   """Migrate an instance.
-- 
1.7.1

Reply via email to