Implement replace secondary via the iallocator
authorIustin Pop <iustin@google.com>
Thu, 24 Apr 2008 11:13:45 +0000 (11:13 +0000)
committerIustin Pop <iustin@google.com>
Thu, 24 Apr 2008 11:13:45 +0000 (11:13 +0000)
This patch implements secondary replace via the iallocator. The new
opcode parameter 'iallocator' behaves like this: if passed, it will
always compute and assign a new secondary, behaving in effect as if the
secondary node has been passed. It conflicts with actually giving the
secondary too.

[Note: not tested with remote_raid1, but the code should behave the
same, we only touch CheckPrereq and we assign a node.]

The patch also adds burnin support for the replace secondary operation;
with this in place, burnin can fully work with auto-assigned nodes.

Reviewed-by: ultrotter

lib/cmdlib.py
lib/opcodes.py
scripts/gnt-instance
tools/burnin

index 7514255..d3d1ca3 100644 (file)
@@ -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:
index 289d971..ad47169 100644 (file)
@@ -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):
index 9a02927..43c4506 100755 (executable)
@@ -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"),
index 23a82e6..3a63d9b 100755 (executable)
@@ -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)