From a123dc19f1b8739dd03908267951bcfd220ed01b Mon Sep 17 00:00:00 2001 From: Michael Hanselmann Date: Fri, 19 Nov 2010 20:41:26 +0100 Subject: [PATCH] Add simple query filter parser This parser reads only the format described by the query2 design document: either an empty filter or an OR operator with equality checks as operands. Signed-off-by: Michael Hanselmann Reviewed-by: Iustin Pop --- Makefile.am | 2 ++ lib/qlang.py | 75 +++++++++++++++++++++++++++++++++++++++++ test/ganeti.qlang_unittest.py | 60 +++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 lib/qlang.py create mode 100755 test/ganeti.qlang_unittest.py diff --git a/Makefile.am b/Makefile.am index b51685a..4bfd094 100644 --- a/Makefile.am +++ b/Makefile.am @@ -138,6 +138,7 @@ pkgpython_PYTHON = \ lib/netutils.py \ lib/objects.py \ lib/opcodes.py \ + lib/qlang.py \ lib/query.py \ lib/rpc.py \ lib/runtime.py \ @@ -443,6 +444,7 @@ python_tests = \ test/ganeti.netutils_unittest.py \ test/ganeti.objects_unittest.py \ test/ganeti.opcodes_unittest.py \ + test/ganeti.qlang_unittest.py \ test/ganeti.query_unittest.py \ test/ganeti.rapi.client_unittest.py \ test/ganeti.rapi.resources_unittest.py \ diff --git a/lib/qlang.py b/lib/qlang.py new file mode 100644 index 0000000..ec4f41b --- /dev/null +++ b/lib/qlang.py @@ -0,0 +1,75 @@ +# +# + +# Copyright (C) 2010 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Module for a simple query language""" + +from ganeti import errors + + +OP_OR = "|" +OP_EQUAL = "=" + + +def ReadSimpleFilter(namefield, filter_): + """Function extracting wanted names from restricted filter. + + This should only be used until proper filtering is implemented. The filter + must either be empty or of the format C{["|", ["=", field, "name1"], ["=", + field, "name2"], ...]}. + + """ + if filter_ is None: + return [] + + if not isinstance(filter_, list): + raise errors.ParameterError("Filter should be list") + + if not filter_ or filter_[0] != OP_OR: + raise errors.ParameterError("Filter should start with OR operator") + + if len(filter_) < 2: + raise errors.ParameterError("Invalid filter, OR operator should have" + " operands") + + result = [] + + for idx, item in enumerate(filter_[1:]): + if not isinstance(item, list): + raise errors.ParameterError("Invalid OR operator, operand %s not a" + " list" % idx) + + if len(item) != 3 or item[0] != OP_EQUAL: + raise errors.ParameterError("Invalid OR operator, operand %s is not an" + " equality filter" % idx) + + (_, name, value) = item + + if not isinstance(value, basestring): + raise errors.ParameterError("Operand %s for OR should compare against a" + " string" % idx) + + if name != namefield: + raise errors.ParameterError("Operand %s for OR should filter field '%s'," + " not '%s'" % (idx, namefield, name)) + + result.append(value) + + return result diff --git a/test/ganeti.qlang_unittest.py b/test/ganeti.qlang_unittest.py new file mode 100755 index 0000000..c0ab03d --- /dev/null +++ b/test/ganeti.qlang_unittest.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# + +# Copyright (C) 2010 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing ganeti.qlang""" + +import unittest + +from ganeti import utils +from ganeti import errors +from ganeti import qlang + +import testutils + + +class TestReadSimpleFilter(unittest.TestCase): + def _Test(self, filter_, expected): + self.assertEqual(qlang.ReadSimpleFilter("name", filter_), expected) + + def test(self): + self._Test(None, []) + self._Test(["|", ["=", "name", "xyz"]], ["xyz"]) + + for i in [1, 3, 10, 25, 140]: + self._Test(["|"] + [["=", "name", "node%s" % j] for j in range(i)], + ["node%s" % j for j in range(i)]) + + def testErrors(self): + for i in [123, True, False, "", "Hello World", "a==b", + [], ["x"], ["x", "y", "z"], ["|"], + ["|", ["="]], ["|", "x"], ["|", 123], + ["|", ["=", "otherfield", "xyz"]], + ["|", ["=", "name", "xyz"], "abc"], + ["|", ["=", "name", "xyz", "too", "long"]], + ["|", ["=", "name", []]], + ["|", ["=", "name", 999]], + ["|", ["=", "name", "abc"], ["=", "otherfield", "xyz"]]]: + self.assertRaises(errors.ParameterError, qlang.ReadSimpleFilter, + "name", i) + + +if __name__ == "__main__": + testutils.GanetiTestProgram() -- 1.7.10.4