4 # Copyright (C) 2010, 2011 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for testing ganeti.qlang"""
27 from ganeti import utils
28 from ganeti import errors
29 from ganeti import qlang
30 from ganeti import query
35 class TestMakeSimpleFilter(unittest.TestCase):
36 def _Test(self, field, names, expected, parse_exp=None):
40 qfilter = qlang.MakeSimpleFilter(field, names)
41 self.assertEqual(qfilter, expected)
44 self._Test("name", None, None, parse_exp=[])
45 self._Test("name", [], None)
46 self._Test("name", ["node1.example.com"],
47 ["|", ["=", "name", "node1.example.com"]])
48 self._Test("xyz", ["a", "b", "c"],
49 ["|", ["=", "xyz", "a"], ["=", "xyz", "b"], ["=", "xyz", "c"]])
52 class TestParseFilter(unittest.TestCase):
54 self.parser = qlang.BuildFilterParser()
56 def _Test(self, qfilter, expected, expect_filter=True):
57 self.assertEqual(qlang.MakeFilter([qfilter], not expect_filter), expected)
58 self.assertEqual(qlang.ParseFilter(qfilter, parser=self.parser), expected)
61 self._Test("name==\"foobar\"", [qlang.OP_EQUAL, "name", "foobar"])
62 self._Test("name=='foobar'", [qlang.OP_EQUAL, "name", "foobar"])
64 self._Test("valA==1 and valB==2 or valC==3",
66 [qlang.OP_AND, [qlang.OP_EQUAL, "valA", 1],
67 [qlang.OP_EQUAL, "valB", 2]],
68 [qlang.OP_EQUAL, "valC", 3]])
70 self._Test(("(name\n==\"foobar\") and (xyz==\"va)ue\" and k == 256 or"
71 " x ==\t\"y\"\n) and mc"),
73 [qlang.OP_EQUAL, "name", "foobar"],
75 [qlang.OP_AND, [qlang.OP_EQUAL, "xyz", "va)ue"],
76 [qlang.OP_EQUAL, "k", 256]],
77 [qlang.OP_EQUAL, "x", "y"]],
78 [qlang.OP_TRUE, "mc"]])
80 self._Test("(xyz==\"v\" or k == 256 and x == \"y\")",
82 [qlang.OP_EQUAL, "xyz", "v"],
83 [qlang.OP_AND, [qlang.OP_EQUAL, "k", 256],
84 [qlang.OP_EQUAL, "x", "y"]]])
86 self._Test("valA==1 and valB==2 and valC==3",
87 [qlang.OP_AND, [qlang.OP_EQUAL, "valA", 1],
88 [qlang.OP_EQUAL, "valB", 2],
89 [qlang.OP_EQUAL, "valC", 3]])
90 self._Test("master or field",
91 [qlang.OP_OR, [qlang.OP_TRUE, "master"],
92 [qlang.OP_TRUE, "field"]])
93 self._Test("mem == 128", [qlang.OP_EQUAL, "mem", 128])
94 self._Test("negfield != -1", [qlang.OP_NOT_EQUAL, "negfield", -1])
95 self._Test("master", [qlang.OP_TRUE, "master"],
97 self._Test("not master", [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
98 for op in ["not", "and", "or"]:
99 self._Test("%sxyz" % op, [qlang.OP_TRUE, "%sxyz" % op],
101 self._Test("not %sxyz" % op,
102 [qlang.OP_NOT, [qlang.OP_TRUE, "%sxyz" % op]])
103 self._Test(" not \t%sfoo" % op,
104 [qlang.OP_NOT, [qlang.OP_TRUE, "%sfoo" % op]])
105 self._Test("%sname =~ m/abc/" % op,
106 [qlang.OP_REGEXP, "%sname" % op, "abc"])
107 self._Test("master and not other",
108 [qlang.OP_AND, [qlang.OP_TRUE, "master"],
109 [qlang.OP_NOT, [qlang.OP_TRUE, "other"]]])
110 self._Test("not (master or other == 4)",
112 [qlang.OP_OR, [qlang.OP_TRUE, "master"],
113 [qlang.OP_EQUAL, "other", 4]]])
114 self._Test("some==\"val\\\"ue\"", [qlang.OP_EQUAL, "some", "val\\\"ue"])
115 self._Test("123 in ips", [qlang.OP_CONTAINS, "ips", 123])
116 self._Test("99 not in ips", [qlang.OP_NOT, [qlang.OP_CONTAINS, "ips", 99]])
117 self._Test("\"a\" in valA and \"b\" not in valB",
118 [qlang.OP_AND, [qlang.OP_CONTAINS, "valA", "a"],
119 [qlang.OP_NOT, [qlang.OP_CONTAINS, "valB", "b"]]])
121 self._Test("name =~ m/test/", [qlang.OP_REGEXP, "name", "test"])
122 self._Test("name =~ m/^node.*example.com$/i",
123 [qlang.OP_REGEXP, "name", "(?i)^node.*example.com$"])
124 self._Test("(name =~ m/^node.*example.com$/s and master) or pip =~ |^3.*|",
127 [qlang.OP_REGEXP, "name", "(?s)^node.*example.com$"],
128 [qlang.OP_TRUE, "master"]],
129 [qlang.OP_REGEXP, "pip", "^3.*"]])
130 for flags in ["si", "is", "ssss", "iiiisiii"]:
131 self._Test("name =~ m/gi/%s" % flags,
132 [qlang.OP_REGEXP, "name", "(?%s)gi" % "".join(sorted(flags))])
134 for i in qlang._KNOWN_REGEXP_DELIM:
135 self._Test("name =~ m%stest%s" % (i, i),
136 [qlang.OP_REGEXP, "name", "test"])
137 self._Test("name !~ m%stest%s" % (i, i),
138 [qlang.OP_NOT, [qlang.OP_REGEXP, "name", "test"]])
139 self._Test("not\tname =~ m%stest%s" % (i, i),
140 [qlang.OP_NOT, [qlang.OP_REGEXP, "name", "test"]])
141 self._Test("notname =~ m%stest%s" % (i, i),
142 [qlang.OP_REGEXP, "notname", "test"])
144 self._Test("name =* '*.site'",
145 [qlang.OP_REGEXP, "name", utils.DnsNameGlobPattern("*.site")])
146 self._Test("field !* '*.example.*'",
147 [qlang.OP_NOT, [qlang.OP_REGEXP, "field",
148 utils.DnsNameGlobPattern("*.example.*")]])
150 def testAllFields(self):
151 for name in frozenset(i for d in query.ALL_FIELD_LISTS for i in d.keys()):
152 self._Test("%s == \"value\"" % name, [qlang.OP_EQUAL, name, "value"])
155 # Invalid field names, meaning no boolean check is done
156 tests = ["#invalid!filter#", "m/x/,"]
158 # Unknown regexp flag
159 tests.append("name=~m#a#g")
161 # Incomplete regexp group
162 tests.append("name=~^[^")
164 # Valid flag, but in uppercase
165 tests.append("asdf =~ m|abc|I")
167 # Non-matching regexp delimiters
168 tests.append("name =~ /foobarbaz#")
170 for qfilter in tests:
172 qlang.ParseFilter(qfilter, parser=self.parser)
173 except errors.QueryFilterParseError, err:
174 self.assertEqual(len(err.GetDetails()), 3)
176 self.fail("Invalid filter '%s' did not raise exception" % qfilter)
179 class TestMakeFilter(unittest.TestCase):
180 def testNoNames(self):
181 self.assertEqual(qlang.MakeFilter([], False), None)
182 self.assertEqual(qlang.MakeFilter(None, False), None)
184 def testPlainNames(self):
185 self.assertEqual(qlang.MakeFilter(["web1", "web2"], False),
186 [qlang.OP_OR, [qlang.OP_EQUAL, "name", "web1"],
187 [qlang.OP_EQUAL, "name", "web2"]])
189 def testForcedFilter(self):
190 for i in [None, [], ["1", "2"], ["", "", ""], ["a", "b", "c", "d"]]:
191 self.assertRaises(errors.OpPrereqError, qlang.MakeFilter, i, True)
193 # Glob pattern shouldn't parse as filter
194 self.assertRaises(errors.QueryFilterParseError,
195 qlang.MakeFilter, ["*.site"], True)
197 # Plain name parses as boolean filter
198 self.assertEqual(qlang.MakeFilter(["web1"], True), [qlang.OP_TRUE, "web1"])
200 def testFilter(self):
201 self.assertEqual(qlang.MakeFilter(["foo/bar"], False),
202 [qlang.OP_TRUE, "foo/bar"])
203 self.assertEqual(qlang.MakeFilter(["foo=='bar'"], False),
204 [qlang.OP_EQUAL, "foo", "bar"])
205 self.assertEqual(qlang.MakeFilter(["field=*'*.site'"], False),
206 [qlang.OP_REGEXP, "field",
207 utils.DnsNameGlobPattern("*.site")])
209 # Plain name parses as name filter, not boolean
210 for name in ["node1", "n-o-d-e", "n_o_d_e", "node1.example.com",
211 "node1.example.com."]:
212 self.assertEqual(qlang.MakeFilter([name], False),
213 [qlang.OP_OR, [qlang.OP_EQUAL, "name", name]])
216 for i in ["foo==bar", "foo+=1"]:
217 self.assertRaises(errors.QueryFilterParseError,
218 qlang.MakeFilter, [i], False)
221 self.assertEqual(qlang.MakeFilter(["*.site"], False),
222 [qlang.OP_OR, [qlang.OP_REGEXP, "name",
223 utils.DnsNameGlobPattern("*.site")]])
224 self.assertEqual(qlang.MakeFilter(["web?.example"], False),
225 [qlang.OP_OR, [qlang.OP_REGEXP, "name",
226 utils.DnsNameGlobPattern("web?.example")]])
227 self.assertEqual(qlang.MakeFilter(["*.a", "*.b", "?.c"], False),
229 [qlang.OP_REGEXP, "name",
230 utils.DnsNameGlobPattern("*.a")],
231 [qlang.OP_REGEXP, "name",
232 utils.DnsNameGlobPattern("*.b")],
233 [qlang.OP_REGEXP, "name",
234 utils.DnsNameGlobPattern("?.c")]])
237 if __name__ == "__main__":
238 testutils.GanetiTestProgram()