HTYPE = constants.HTYPE_INSTANCE
_OP_REQP = ["instance_name", "mode", "disks"]
+ def _RunAllocator(self):
+ """Compute a new secondary node using an IAllocator.
+
+ """
+ ial = IAllocator(self.cfg, self.sstore,
+ mode=constants.IALLOCATOR_MODE_RELOC,
+ name=self.op.instance_name,
+ relocate_from=[self.sec_node])
+
+ ial.Run(self.op.iallocator)
+
+ if not ial.success:
+ raise errors.OpPrereqError("Can't compute nodes using"
+ " iallocator '%s': %s" % (self.op.iallocator,
+ ial.info))
+ if len(ial.nodes) != ial.required_nodes:
+ raise errors.OpPrereqError("iallocator '%s' returned invalid number"
+ " of nodes (%s), required %s" %
+ (len(ial.nodes), ial.required_nodes))
+ self.op.remote_node = ial.nodes[0]
+ logger.ToStdout("Selected new secondary for the instance: %s" %
+ self.op.remote_node)
+
def BuildHooksEnv(self):
"""Build hooks env.
This checks that the instance is in the cluster.
"""
+ if not hasattr(self.op, "remote_node"):
+ self.op.remote_node = None
+
instance = self.cfg.GetInstanceInfo(
self.cfg.ExpandInstanceName(self.op.instance_name))
if instance is None:
self.sec_node = instance.secondary_nodes[0]
- remote_node = getattr(self.op, "remote_node", None)
+ ia_name = getattr(self.op, "iallocator", None)
+ if ia_name is not None:
+ if self.op.remote_node is not None:
+ raise errors.OpPrereqError("Give either the iallocator or the new"
+ " secondary, not both")
+ self.op.remote_node = self._RunAllocator()
+
+ remote_node = self.op.remote_node
if remote_node is not None:
remote_node = self.cfg.ExpandNodeName(remote_node)
if remote_node is None:
class OpReplaceDisks(OpCode):
"""Replace the disks of an instance."""
OP_ID = "OP_INSTANCE_REPLACE_DISKS"
- __slots__ = ["instance_name", "remote_node", "mode", "disks"]
+ __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
class OpFailoverInstance(OpCode):
"""
instance_name = args[0]
new_2ndary = opts.new_secondary
+ iallocator = opts.iallocator
if opts.disks is None:
disks = ["sda", "sdb"]
else:
mode = constants.REPLACE_DISK_ALL
elif opts.on_primary: # only on primary:
mode = constants.REPLACE_DISK_PRI
- if new_2ndary is not None:
+ if new_2ndary is not None or iallocator is not None:
raise errors.OpPrereqError("Can't change secondary node on primary disk"
" replacement")
- elif opts.on_secondary is not None: # only on secondary
+ elif opts.on_secondary is not None or iallocator is not None:
+ # only on secondary
mode = constants.REPLACE_DISK_SEC
op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
- remote_node=new_2ndary, mode=mode)
+ remote_node=new_2ndary, mode=mode,
+ iallocator=iallocator)
SubmitOpCode(op)
return 0
help=("Comma-separated list of disks"
" to replace (e.g. sda) (optional,"
" defaults to all disks")),
+ make_option("--iallocator", metavar="<NAME>",
+ help="Select new secondary for the instance"
+ " automatically using the"
+ " <NAME> iallocator plugin (enables"
+ " secondary node replacement)",
+ default=None, type="string"),
],
"[-s|-p|-n NODE] <instance>",
"Replaces all disks for the instance"),
mytor = izip(islice(cycle(self.nodes), 2, None),
self.instances)
for tnode, instance in mytor:
+ if self.opts.iallocator:
+ tnode = None
op = opcodes.OpReplaceDisks(instance_name=instance,
mode=mode,
remote_node=tnode,
+ iallocator=self.opts.iallocator,
disks=["sda", "sdb"])
Log("- Replace secondary (%s) for instance %s" % (mode, instance))
self.ExecOp(op)