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