return default
+class _QaNode(object):
+ __slots__ = [
+ "primary",
+ "secondary",
+ "_added",
+ "use_count",
+ ]
+
+ def __init__(self, primary, secondary):
+ """Initializes instances of this class.
+
+ """
+ self.primary = primary
+ self.secondary = secondary
+ self.use_count = 0
+ self._added = False
+
+ @classmethod
+ def FromDict(cls, data):
+ """Creates node object from JSON dictionary.
+
+ """
+ return cls(primary=data["primary"], secondary=data.get("secondary"))
+
+ def __getitem__(self, key):
+ """Legacy dict-like interface.
+
+ """
+ if key == "primary":
+ return self.primary
+ elif key == "secondary":
+ return self.secondary
+ else:
+ raise KeyError(key)
+
+ def get(self, key, default):
+ """Legacy dict-like interface.
+
+ """
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def Use(self):
+ """Marks a node as being in use.
+
+ """
+ assert self.use_count >= 0
+
+ self.use_count += 1
+
+ return self
+
+ def MarkAdded(self):
+ """Marks node as having been added to a cluster.
+
+ """
+ assert not self._added
+ self._added = True
+
+ def MarkRemoved(self):
+ """Marks node as having been removed from a cluster.
+
+ """
+ assert self._added
+ self._added = False
+
+ @property
+ def added(self):
+ """Returns whether a node is part of a cluster.
+
+ """
+ return self._added
+
+
_RESOURCE_CONVERTER = {
"instances": _QaInstance.FromDict,
+ "nodes": _QaNode.FromDict,
}
return GetConfig().IsTemplateSupported(templ)
-def AcquireNode(exclude=None):
+def AcquireNode(exclude=None, _cfg=None):
"""Returns the least used node.
"""
- master = GetMasterNode()
- cfg = GetConfig()
+ if _cfg is None:
+ cfg = GetConfig()
+ else:
+ cfg = _cfg
+
+ master = cfg.GetMasterNode()
# Filter out unwanted nodes
# TODO: Maybe combine filters
else:
nodes = filter(lambda node: node != exclude, cfg["nodes"])
- tmp_flt = lambda node: node.get("_added", False) or node == master
- nodes = filter(tmp_flt, nodes)
- del tmp_flt
+ nodes = filter(lambda node: node.added or node == master, nodes)
- if len(nodes) == 0:
+ if not nodes:
raise qa_error.OutOfNodesError("No nodes left")
# Get node with least number of uses
+ # TODO: Switch to computing sort key instead of comparing directly
def compare(a, b):
- result = cmp(a.get("_count", 0), b.get("_count", 0))
+ result = cmp(a.use_count, b.use_count)
if result == 0:
- result = cmp(a["primary"], b["primary"])
+ result = cmp(a.primary, b.primary)
return result
nodes.sort(cmp=compare)
- node = nodes[0]
- node["_count"] = node.get("_count", 0) + 1
- return node
+ return nodes[0].Use()
def AcquireManyNodes(num, exclude=None):
def ReleaseNode(node):
- node["_count"] = node.get("_count", 0) - 1
+ assert node.use_count > 0
+
+ node.use_count -= 1
def ReleaseManyNodes(nodes):
def _NodeAdd(node, readd=False):
- if not readd and node.get("_added", False):
+ if not readd and node.added:
raise qa_error.Error("Node %s already in cluster" % node["primary"])
- elif readd and not node.get("_added", False):
+ elif readd and not node.added:
raise qa_error.Error("Node %s not yet in cluster" % node["primary"])
cmd = ["gnt-node", "add", "--no-ssh-key-check"]
AssertCommand(cmd)
- node["_added"] = True
+ if readd:
+ assert node.added
+ else:
+ node.MarkAdded()
def _NodeRemove(node):
AssertCommand(["gnt-node", "remove", node["primary"]])
- node["_added"] = False
+ node.MarkRemoved()
def MakeNodeOffline(node, value):
master = qa_config.GetMasterNode()
for node in qa_config.get("nodes"):
if node != master:
- node["_added"] = True
+ node.MarkAdded()
def TestNodeRemoveAll():
self.assertTrue(isinstance(self.config["instances"][0],
qa_config._QaInstance))
+ def testNodeConversion(self):
+ self.assertTrue(isinstance(self.config["nodes"][0],
+ qa_config._QaNode))
+
def testAcquireAndReleaseInstance(self):
self.assertFalse(compat.any(map(operator.attrgetter("used"),
self.config["instances"])))
self.assertRaises(qa_error.OutOfInstancesError,
qa_config.AcquireInstance, _cfg=self.config)
+ def testAcquireNodeNoneAdded(self):
+ self.assertFalse(compat.any(map(operator.attrgetter("added"),
+ self.config["nodes"])))
+
+ # First call must return master node
+ node = qa_config.AcquireNode(_cfg=self.config)
+ self.assertEqual(node, self.config.GetMasterNode())
+
+ # Next call with exclusion list fails
+ self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
+ exclude=[node], _cfg=self.config)
+
+ def testAcquireNodeTooMany(self):
+ # Mark all nodes as marked (master excluded)
+ for node in self.config["nodes"]:
+ if node != self.config.GetMasterNode():
+ node.MarkAdded()
+
+ nodecount = len(self.config["nodes"])
+
+ self.assertTrue(nodecount > 1)
+
+ acquired = []
+
+ for _ in range(nodecount):
+ node = qa_config.AcquireNode(exclude=acquired, _cfg=self.config)
+ if node == self.config.GetMasterNode():
+ self.assertFalse(node.added)
+ else:
+ self.assertTrue(node.added)
+ self.assertEqual(node.use_count, 1)
+ acquired.append(node)
+
+ self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
+ exclude=acquired, _cfg=self.config)
+
if __name__ == "__main__":
testutils.GanetiTestProgram()