From 4b7735f913d2c5ca556098f5a33f849e667f4f16 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Tue, 2 Dec 2008 05:06:08 +0000
Subject: [PATCH] Add cluster candidate pool size parameter

This patch adds a new cluster paramater "candidate_pool_size" which
tracks the desired size of the list of nodes with the master_candidate
flag set.

Reviewed-by: imsnah
---
 lib/cmdlib.py       | 44 ++++++++++++++++++++++++++++++++++++++++++++
 lib/objects.py      |  1 +
 lib/opcodes.py      |  8 +++++++-
 scripts/gnt-cluster | 11 +++++++++--
 4 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 491e695f8..c6850ff2f 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -32,6 +32,7 @@ import re
 import platform
 import logging
 import copy
+import random
 
 from ganeti import ssh
 from ganeti import utils
@@ -1186,6 +1187,21 @@ class LUSetClusterParams(LogicalUnit):
   _OP_REQP = []
   REQ_BGL = False
 
+  def CheckParameters(self):
+    """Check parameters
+
+    """
+    if not hasattr(self.op, "candidate_pool_size"):
+      self.op.candidate_pool_size = None
+    if self.op.candidate_pool_size is not None:
+      try:
+        self.op.candidate_pool_size = int(self.op.candidate_pool_size)
+      except ValueError, err:
+        raise errors.OpPrereqError("Invalid candidate_pool_size value: %s" %
+                                   str(err))
+      if self.op.candidate_pool_size < 1:
+        raise errors.OpPrereqError("At least one master candidate needed")
+
   def ExpandNames(self):
     # FIXME: in the future maybe other cluster params won't require checking on
     # all nodes to be modified.
@@ -1284,8 +1300,35 @@ class LUSetClusterParams(LogicalUnit):
       self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
     if self.op.beparams:
       self.cluster.beparams[constants.BEGR_DEFAULT] = self.new_beparams
+    if self.op.candidate_pool_size is not None:
+      self.cluster.candidate_pool_size = self.op.candidate_pool_size
+
     self.cfg.Update(self.cluster)
 
+    # we want to update nodes after the cluster so that if any errors
+    # happen, we have recorded and saved the cluster info
+    if self.op.candidate_pool_size is not None:
+      node_info = self.cfg.GetAllNodesInfo().values()
+      num_candidates = len([node for node in node_info
+                            if node.master_candidate])
+      num_nodes = len(node_info)
+      if num_candidates < self.op.candidate_pool_size:
+        random.shuffle(node_info)
+        for node in node_info:
+          if num_candidates >= self.op.candidate_pool_size:
+            break
+          if node.master_candidate:
+            continue
+          node.master_candidate = True
+          self.LogInfo("Promoting node %s to master candidate", node.name)
+          self.cfg.Update(node)
+          self.context.ReaddNode(node)
+          num_candidates += 1
+      elif num_candidates > self.op.candidate_pool_size:
+        self.LogInfo("Note: more nodes are candidates (%d) than the new value"
+                     " of candidate_pool_size (%d)" %
+                     (num_candidates, self.op.candidate_pool_size))
+
 
 def _WaitForSync(lu, instance, oneshot=False, unlock=False):
   """Sleep and poll for an instance's disk to sync.
@@ -2058,6 +2101,7 @@ class LUQueryClusterInfo(NoHooksLU):
       "enabled_hypervisors": cluster.enabled_hypervisors,
       "hvparams": cluster.hvparams,
       "beparams": cluster.beparams,
+      "candidate_pool_size": cluster.candidate_pool_size,
       }
 
     return result
diff --git a/lib/objects.py b/lib/objects.py
index 7e8de2a6d..abfeff125 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -707,6 +707,7 @@ class Cluster(TaggableObject):
     "enabled_hypervisors",
     "hvparams",
     "beparams",
+    "candidate_pool_size",
     ]
 
   def ToDict(self):
diff --git a/lib/opcodes.py b/lib/opcodes.py
index c0b88a91a..958ee4261 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -255,7 +255,13 @@ class OpSetClusterParams(OpCode):
 
   """
   OP_ID = "OP_CLUSTER_SET_PARAMS"
-  __slots__ = ["vg_name", "enabled_hypervisors", "hvparams", "beparams"]
+  __slots__ = [
+    "vg_name",
+    "enabled_hypervisors",
+    "hvparams",
+    "beparams",
+    "candidate_pool_size",
+    ]
 
 
 # node opcodes
diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster
index fcf0fc91e..53cb6a38c 100755
--- a/scripts/gnt-cluster
+++ b/scripts/gnt-cluster
@@ -238,6 +238,9 @@ def ShowClusterConfig(opts, args):
       ToStdout("      %s: %s", item, val)
 
   ToStdout("Cluster parameters:")
+  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
+
+  ToStdout("Default instance parameters:")
   for gr_name, gr_dict in result["beparams"].items():
     ToStdout("  - %s:", gr_name)
     for item, val in gr_dict.iteritems():
@@ -448,7 +451,7 @@ def SetClusterParams(opts, args):
   """
   if not (not opts.lvm_storage or opts.vg_name or
           opts.enabled_hypervisors or opts.hvparams or
-          opts.beparams):
+          opts.beparams or opts.candidate_pool_size is not None):
     ToStderr("Please give at least one of the parameters.")
     return 1
 
@@ -471,7 +474,8 @@ def SetClusterParams(opts, args):
   op = opcodes.OpSetClusterParams(vg_name=opts.vg_name,
                                   enabled_hypervisors=hvlist,
                                   hvparams=hvparams,
-                                  beparams=beparams)
+                                  beparams=beparams,
+                                  candidate_pool_size=opts.candidate_pool_size)
   SubmitOpCode(op)
   return 0
 
@@ -636,6 +640,9 @@ commands = {
               keyval_option("-B", "--backend-parameters", dest="beparams",
                             type="keyval", default={},
                             help="Backend parameters"),
+              make_option("-C", "--candidate-pool-size", default=None,
+                          help="Set the candidate pool size",
+                          dest="candidate_pool_size", type="int"),
               ],
              "[opts...]",
              "Alters the parameters of the cluster"),
-- 
GitLab