diff --git a/lib/cmdlib.py b/lib/cmdlib.py index a45c268a4c09c1cdbe6e7b6b0a9dc30c5589a1be..0ecaa0c9a0bb40b60c6b6e47917d853e99b46023 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -709,6 +709,19 @@ def _CheckInstanceBridgesExist(lu, instance, node=None): _CheckNicsBridgesExist(lu, instance.nics, node) +def _GetNodePrimaryInstances(cfg, node_name): + """Returns primary instances on a node. + + """ + instances = [] + + for (_, inst) in cfg.GetAllInstancesInfo().iteritems(): + if node_name == inst.primary_node: + instances.append(inst) + + return instances + + def _GetNodeSecondaryInstances(cfg, node_name): """Returns secondary instances on a node. @@ -3941,6 +3954,60 @@ class LUMigrateInstance(LogicalUnit): return env, nl, nl +class LUMigrateNode(LogicalUnit): + """Migrate all instances from a node. + + """ + HPATH = "node-migrate" + HTYPE = constants.HTYPE_NODE + _OP_REQP = ["node_name", "live"] + REQ_BGL = False + + 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 = { + locking.LEVEL_NODE: [self.op.node_name], + } + + self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND + + # Create tasklets for migrating instances for all instances on this node + names = [] + tasklets = [] + + for inst in _GetNodePrimaryInstances(self.cfg, self.op.node_name): + logging.debug("Migrating instance %s", inst.name) + names.append(inst.name) + + tasklets.append(TLMigrateInstance(self, inst.name, self.op.live, False)) + + self.tasklets = tasklets + + # Declare instance locks + self.needed_locks[locking.LEVEL_INSTANCE] = names + + def DeclareLocks(self, level): + if level == locking.LEVEL_NODE: + self._LockInstancesNodes() + + 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()] + + return (env, nl, nl) + + class TLMigrateInstance(Tasklet): def __init__(self, lu, instance_name, live, cleanup): """Initializes this class. @@ -4253,6 +4320,8 @@ class TLMigrateInstance(Tasklet): """Perform the migration. """ + feedback_fn("Migrating instance %s" % self.instance.name) + self.feedback_fn = feedback_fn self.source_node = self.instance.primary_node diff --git a/lib/mcpu.py b/lib/mcpu.py index 27e5ca3562978fb48bfbdb5a2ae42737d9a3b43e..ef230918e74e9889b0c92974920d59abaaedf244 100644 --- a/lib/mcpu.py +++ b/lib/mcpu.py @@ -58,6 +58,7 @@ class Processor(object): opcodes.OpSetNodeParams: cmdlib.LUSetNodeParams, opcodes.OpPowercycleNode: cmdlib.LUPowercycleNode, opcodes.OpEvacuateNode: cmdlib.LUEvacuateNode, + opcodes.OpMigrateNode: cmdlib.LUMigrateNode, # instance lu opcodes.OpCreateInstance: cmdlib.LUCreateInstance, opcodes.OpReinstallInstance: cmdlib.LUReinstallInstance, diff --git a/lib/opcodes.py b/lib/opcodes.py index e49d0be8d96988b4d0d6e5e656104b27e3a61b06..f39d9cb3991c23f5bb66f5d7a0dd6902c665d18f 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -360,6 +360,16 @@ class OpEvacuateNode(OpCode): ] +class OpMigrateNode(OpCode): + """Migrate all instances from a node.""" + OP_ID = "OP_NODE_MIGRATE" + OP_DSC_FIELD = "node_name" + __slots__ = OpCode.__slots__ + [ + "node_name", + "live", + ] + + # instance opcodes class OpCreateInstance(OpCode):