Commit 05484a24 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

cli.GetOnlineNodes: Support node group filter, use query2



This patc changes cli.GetOnlineNodes to use query2, which does the
filtering in the master daemon, and adds a new parameter to filter by
node group.

Unittests were added for the old implementation and then adopted to
ensure no functionality was lost.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 172679c9
......@@ -2868,7 +2868,7 @@ def ParseTimespec(value):
def GetOnlineNodes(nodes, cl=None, nowarn=False, secondary_ips=False,
filter_master=False):
filter_master=False, nodegroup=None):
"""Returns the names of online nodes.
This function will also log a warning on stderr with the names of
......@@ -2889,28 +2889,60 @@ def GetOnlineNodes(nodes, cl=None, nowarn=False, secondary_ips=False,
@param filter_master: if True, do not return the master node in the list
(useful in coordination with secondary_ips where we cannot check our
node name against the list)
@type nodegroup: string
@param nodegroup: If set, only return nodes in this node group
"""
if cl is None:
cl = GetClient()
if secondary_ips:
name_idx = 2
else:
name_idx = 0
filter_ = []
if nodes:
filter_.append(qlang.MakeSimpleFilter("name", nodes))
if nodegroup is not None:
filter_.append([qlang.OP_OR, [qlang.OP_EQUAL, "group", nodegroup],
[qlang.OP_EQUAL, "group.uuid", nodegroup]])
if filter_master:
master_node = cl.QueryConfigValues(["master_node"])[0]
filter_fn = lambda x: x != master_node
filter_.append([qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
if filter_:
if len(filter_) > 1:
final_filter = [qlang.OP_AND] + filter_
else:
assert len(filter_) == 1
final_filter = filter_[0]
else:
filter_fn = lambda _: True
final_filter = None
result = cl.Query(constants.QR_NODE, ["name", "offline", "sip"], final_filter)
def _IsOffline(row):
(_, (_, offline), _) = row
return offline
def _GetName(row):
((_, name), _, _) = row
return name
def _GetSip(row):
(_, _, (_, sip)) = row
return sip
(offline, online) = compat.partition(result.data, _IsOffline)
result = cl.QueryNodes(names=nodes, fields=["name", "offline", "sip"],
use_locking=False)
offline = [row[0] for row in result if row[1]]
if offline and not nowarn:
ToStderr("Note: skipping offline node(s): %s" % utils.CommaJoin(offline))
return [row[name_idx] for row in result if not row[1] and filter_fn(row[0])]
ToStderr("Note: skipping offline node(s): %s" %
utils.CommaJoin(map(_GetName, offline)))
if secondary_ips:
fn = _GetSip
else:
fn = _GetName
return map(fn, online)
def _ToStream(stream, txt, *args):
......
......@@ -32,6 +32,7 @@ from ganeti import cli
from ganeti import errors
from ganeti import utils
from ganeti import objects
from ganeti import qlang
from ganeti.errors import OpPrereqError, ParameterError
......@@ -753,5 +754,147 @@ class TestFormatResultError(unittest.TestCase):
self.assertTrue(result.endswith(")"))
class TestGetOnlineNodes(unittest.TestCase):
class _FakeClient:
def __init__(self):
self._query = []
def AddQueryResult(self, *args):
self._query.append(args)
def CountPending(self):
return len(self._query)
def Query(self, res, fields, filter_):
if res != constants.QR_NODE:
raise Exception("Querying wrong resource")
(exp_fields, check_filter, result) = self._query.pop(0)
if exp_fields != fields:
raise Exception("Expected fields %s, got %s" % (exp_fields, fields))
if not (filter_ is None or check_filter(filter_)):
raise Exception("Filter doesn't match expectations")
return objects.QueryResponse(fields=None, data=result)
def testEmpty(self):
cl = self._FakeClient()
cl.AddQueryResult(["name", "offline", "sip"], None, [])
self.assertEqual(cli.GetOnlineNodes(None, cl=cl), [])
self.assertEqual(cl.CountPending(), 0)
def testNoSpecialFilter(self):
cl = self._FakeClient()
cl.AddQueryResult(["name", "offline", "sip"], None, [
[(constants.RS_NORMAL, "master.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.1")],
[(constants.RS_NORMAL, "node2.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.2")],
])
self.assertEqual(cli.GetOnlineNodes(None, cl=cl),
["master.example.com", "node2.example.com"])
self.assertEqual(cl.CountPending(), 0)
def testNoMaster(self):
cl = self._FakeClient()
def _CheckFilter(filter_):
self.assertEqual(filter_, [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
return True
cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
[(constants.RS_NORMAL, "node2.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.2")],
])
self.assertEqual(cli.GetOnlineNodes(None, cl=cl, filter_master=True),
["node2.example.com"])
self.assertEqual(cl.CountPending(), 0)
def testSecondaryIpAddress(self):
cl = self._FakeClient()
cl.AddQueryResult(["name", "offline", "sip"], None, [
[(constants.RS_NORMAL, "master.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.1")],
[(constants.RS_NORMAL, "node2.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.2")],
])
self.assertEqual(cli.GetOnlineNodes(None, cl=cl, secondary_ips=True),
["192.0.2.1", "192.0.2.2"])
self.assertEqual(cl.CountPending(), 0)
def testNoMasterFilterNodeName(self):
cl = self._FakeClient()
def _CheckFilter(filter_):
self.assertEqual(filter_,
[qlang.OP_AND,
[qlang.OP_OR] + [[qlang.OP_EQUAL, "name", name]
for name in ["node2", "node3"]],
[qlang.OP_NOT, [qlang.OP_TRUE, "master"]]])
return True
cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
[(constants.RS_NORMAL, "node2.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.12")],
[(constants.RS_NORMAL, "node3.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.13")],
])
self.assertEqual(cli.GetOnlineNodes(["node2", "node3"], cl=cl,
secondary_ips=True, filter_master=True),
["192.0.2.12", "192.0.2.13"])
self.assertEqual(cl.CountPending(), 0)
def testOfflineNodes(self):
cl = self._FakeClient()
cl.AddQueryResult(["name", "offline", "sip"], None, [
[(constants.RS_NORMAL, "master.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.1")],
[(constants.RS_NORMAL, "node2.example.com"),
(constants.RS_NORMAL, True),
(constants.RS_NORMAL, "192.0.2.2")],
[(constants.RS_NORMAL, "node3.example.com"),
(constants.RS_NORMAL, True),
(constants.RS_NORMAL, "192.0.2.3")],
])
self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nowarn=True),
["master.example.com"])
self.assertEqual(cl.CountPending(), 0)
def testNodeGroup(self):
cl = self._FakeClient()
def _CheckFilter(filter_):
self.assertEqual(filter_,
[qlang.OP_OR, [qlang.OP_EQUAL, "group", "foobar"],
[qlang.OP_EQUAL, "group.uuid", "foobar"]])
return True
cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
[(constants.RS_NORMAL, "master.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.1")],
[(constants.RS_NORMAL, "node3.example.com"),
(constants.RS_NORMAL, False),
(constants.RS_NORMAL, "192.0.2.3")],
])
self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nodegroup="foobar"),
["master.example.com", "node3.example.com"])
self.assertEqual(cl.CountPending(), 0)
if __name__ == '__main__':
testutils.GanetiTestProgram()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment