diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 7514255aa899b4c9091d87767d1844b878229983..d3d1ca3ee0e42f5058585b802a342e40f7f89a00 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -3571,6 +3571,29 @@ class LUReplaceDisks(LogicalUnit): 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. @@ -3597,6 +3620,9 @@ class LUReplaceDisks(LogicalUnit): 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: @@ -3616,7 +3642,14 @@ class LUReplaceDisks(LogicalUnit): 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: diff --git a/lib/opcodes.py b/lib/opcodes.py index 289d971b16d0ed82b024ea5b9e86f23be5c8d6fe..ad4716956f28ebd7bb5a576d6c955e00e4b14363 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -328,7 +328,7 @@ class OpRebootInstance(OpCode): 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): diff --git a/scripts/gnt-instance b/scripts/gnt-instance index 9a02927c873431eb45c1e66977642d58dc91e1de..43c4506eeb5249d207f9fec7755e56c6a3373a5c 100755 --- a/scripts/gnt-instance +++ b/scripts/gnt-instance @@ -448,6 +448,7 @@ def ReplaceDisks(opts, args): """ instance_name = args[0] new_2ndary = opts.new_secondary + iallocator = opts.iallocator if opts.disks is None: disks = ["sda", "sdb"] else: @@ -456,14 +457,16 @@ def ReplaceDisks(opts, args): 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 @@ -841,6 +844,12 @@ commands = { 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"), diff --git a/tools/burnin b/tools/burnin index 23a82e649622e505fc5834bc0fbe4470c2586982..3a63d9b185a62271c245a99d3cb99b5a756ed7b1 100755 --- a/tools/burnin +++ b/tools/burnin @@ -257,9 +257,12 @@ class Burner(object): 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)