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 self._Test("ctime < 1234", [qlang.OP_LT, "ctime", 1234])
151 self._Test("ctime > 1234", [qlang.OP_GT, "ctime", 1234])
152 self._Test("mtime <= 9999", [qlang.OP_LE, "mtime", 9999])
153 self._Test("mtime >= 9999", [qlang.OP_GE, "mtime", 9999])
155 def testAllFields(self):
156 for name in frozenset(i for d in query.ALL_FIELD_LISTS for i in d.keys()):
157 self._Test("%s == \"value\"" % name, [qlang.OP_EQUAL, name, "value"])
160 # Invalid field names, meaning no boolean check is done
161 tests = ["#invalid!filter#", "m/x/,"]
163 # Unknown regexp flag
164 tests.append("name=~m#a#g")
166 # Incomplete regexp group
167 tests.append("name=~^[^")
169 # Valid flag, but in uppercase
170 tests.append("asdf =~ m|abc|I")
172 # Non-matching regexp delimiters
173 tests.append("name =~ /foobarbaz#")
176 tests.append("name <> value")
177 tests.append("name => value")
178 tests.append("name =< value")
180 for qfilter in tests:
182 qlang.ParseFilter(qfilter, parser=self.parser)
183 except errors.QueryFilterParseError, err:
184 self.assertEqual(len(err.GetDetails()), 3)
186 self.fail("Invalid filter '%s' did not raise exception" % qfilter)
189 class TestMakeFilter(unittest.TestCase):
190 def testNoNames(self):
191 self.assertEqual(qlang.MakeFilter([], False), None)
192 self.assertEqual(qlang.MakeFilter(None, False), None)
194 def testPlainNames(self):
195 self.assertEqual(qlang.MakeFilter(["web1", "web2"], False),
196 [qlang.OP_OR, [qlang.OP_EQUAL, "name", "web1"],
197 [qlang.OP_EQUAL, "name", "web2"]])
199 def testPlainNamesOtherNamefield(self):
200 self.assertEqual(qlang.MakeFilter(["mailA", "mailB"], False,
202 [qlang.OP_OR, [qlang.OP_EQUAL, "id", "mailA"],
203 [qlang.OP_EQUAL, "id", "mailB"]])
205 def testForcedFilter(self):
206 for i in [None, [], ["1", "2"], ["", "", ""], ["a", "b", "c", "d"]]:
207 self.assertRaises(errors.OpPrereqError, qlang.MakeFilter, i, True)
209 # Glob pattern shouldn't parse as filter
210 self.assertRaises(errors.QueryFilterParseError,
211 qlang.MakeFilter, ["*.site"], True)
213 # Plain name parses as boolean filter
214 self.assertEqual(qlang.MakeFilter(["web1"], True), [qlang.OP_TRUE, "web1"])
216 def testFilter(self):
217 self.assertEqual(qlang.MakeFilter(["foo/bar"], False),
218 [qlang.OP_TRUE, "foo/bar"])
219 self.assertEqual(qlang.MakeFilter(["foo=='bar'"], False),
220 [qlang.OP_EQUAL, "foo", "bar"])
221 self.assertEqual(qlang.MakeFilter(["field=*'*.site'"], False),
222 [qlang.OP_REGEXP, "field",
223 utils.DnsNameGlobPattern("*.site")])
225 # Plain name parses as name filter, not boolean
226 for name in ["node1", "n-o-d-e", "n_o_d_e", "node1.example.com",
227 "node1.example.com."]:
228 self.assertEqual(qlang.MakeFilter([name], False),
229 [qlang.OP_OR, [qlang.OP_EQUAL, "name", name]])
232 for i in ["foo==bar", "foo+=1"]:
233 self.assertRaises(errors.QueryFilterParseError,
234 qlang.MakeFilter, [i], False)
237 self.assertEqual(qlang.MakeFilter(["*.site"], False),
238 [qlang.OP_OR, [qlang.OP_REGEXP, "name",
239 utils.DnsNameGlobPattern("*.site")]])
240 self.assertEqual(qlang.MakeFilter(["web?.example"], False),
241 [qlang.OP_OR, [qlang.OP_REGEXP, "name",
242 utils.DnsNameGlobPattern("web?.example")]])
243 self.assertEqual(qlang.MakeFilter(["*.a", "*.b", "?.c"], False),
245 [qlang.OP_REGEXP, "name",
246 utils.DnsNameGlobPattern("*.a")],
247 [qlang.OP_REGEXP, "name",
248 utils.DnsNameGlobPattern("*.b")],
249 [qlang.OP_REGEXP, "name",
250 utils.DnsNameGlobPattern("?.c")]])
253 if __name__ == "__main__":
254 testutils.GanetiTestProgram()