diff --git a/Makefile.am b/Makefile.am index b51685a30f80f879d07cee29bfbd492fb89c1f20..4bfd0943819091ebbf476f97b3dce1f05af173bd 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 0000000000000000000000000000000000000000..ec4f41b96aa1b6644fd5eba13dcdb22ec528da80 --- /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 0000000000000000000000000000000000000000..c0ab03d597f80a90f54b3a79c5d70b7e2732c9f1 --- /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()