Commit 7ffc5a86 authored by Michael Hanselmann's avatar Michael Hanselmann

Add new opcode to evacuate nodes

Signed-off-by: default avatarMichael Hanselmann <>
Reviewed-by: default avatarGuido Trotter <>
parent c68174b6
......@@ -145,6 +145,16 @@ Changes a node's parameters.
:pre-execution: master node, the target node
:post-execution: master node, the target node
Relocate secondary instances from a node.
:directory: node-evacuate
:pre-execution: master node, target node
:post-execution: master node, target node
Instance operations
......@@ -5253,6 +5253,97 @@ class LUReplaceDisks(LogicalUnit):
return env, nl, nl
class LUEvacuateNode(LogicalUnit):
"""Relocate the secondary instances from a node.
HPATH = "node-evacuate"
HTYPE = constants.HTYPE_NODE
_OP_REQP = ["node_name"]
REQ_BGL = False
def CheckArguments(self):
if not hasattr(self.op, "remote_node"):
self.op.remote_node = None
if not hasattr(self.op, "iallocator"):
self.op.iallocator = None
def ExpandNames(self):
self.op.node_name = self.cfg.ExpandNodeName(self.op.node_name)
if self.op.node_name is None:
raise errors.OpPrereqError("Node '%s' not known" % self.op.node_name)
self.needed_locks = {}
# Declare node locks
if self.op.iallocator is not None:
self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
elif self.op.remote_node is not None:
remote_node = self.cfg.ExpandNodeName(self.op.remote_node)
if remote_node is None:
raise errors.OpPrereqError("Node '%s' not known" %
self.op.remote_node = remote_node
# Warning: do not remove the locking of the new secondary here
# unless DRBD8.AddChildren is changed to work in parallel;
# currently it doesn't since parallel invocations of
# FindUnusedMinor will conflict
self.needed_locks[locking.LEVEL_NODE] = [remote_node]
self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
raise errors.OpPrereqError("Invalid parameters")
# Create tasklets for replacing disks for all secondary instances on this
# node
names = []
for inst in _GetNodeSecondaryInstances(self.cfg, self.op.node_name):
logging.debug("Replacing disks for instance %s",
replacer = TLReplaceDisks(self,, constants.REPLACE_DISK_CHG,
self.op.iallocator, self.op.remote_node, [])
self.instance_names = names
# Declare instance locks
self.needed_locks[locking.LEVEL_INSTANCE] = self.instance_names
def DeclareLocks(self, level):
# If we're not already locking all nodes in the set we have to declare the
# instance's primary/secondary nodes.
if (level == locking.LEVEL_NODE and
self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET):
def BuildHooksEnv(self):
"""Build hooks env.
This runs on the master, the primary and all the secondaries.
env = {
"NODE_NAME": self.op.node_name,
nl = [self.cfg.GetMasterNode()]
if self.op.remote_node is not None:
env["NEW_SECONDARY"] = self.op.remote_node
return (env, nl, nl)
class TLReplaceDisks(Tasklet):
"""Replaces disks for an instance.
......@@ -5423,6 +5514,8 @@ class TLReplaceDisks(Tasklet):
This dispatches the disk replacement to the appropriate handler.
feedback_fn("Replacing disks for %s" %
activate_disks = (not self.instance.admin_up)
# Activate the instance disks if we're replacing them on a down instance
......@@ -57,6 +57,7 @@ class Processor(object):
opcodes.OpRemoveNode: cmdlib.LURemoveNode,
opcodes.OpSetNodeParams: cmdlib.LUSetNodeParams,
opcodes.OpPowercycleNode: cmdlib.LUPowercycleNode,
opcodes.OpEvacuateNode: cmdlib.LUEvacuateNode,
# instance lu
opcodes.OpCreateInstance: cmdlib.LUCreateInstance,
opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance,
......@@ -350,6 +350,16 @@ class OpPowercycleNode(OpCode):
class OpEvacuateNode(OpCode):
"""Relocate secondary instances from a node."""
OP_DSC_FIELD = "node_name"
__slots__ = OpCode.__slots__ + [
"node_name", "remote_node", "iallocator",
# instance opcodes
class OpCreateInstance(OpCode):
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment