Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.qlang_unittest.py @ ad48eacc

History | View | Annotate | Download (10.2 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2010, 2011 Google Inc.
5
#
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.
10
#
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.
15
#
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
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for testing ganeti.qlang"""
23

    
24
import unittest
25
import string
26

    
27
from ganeti import utils
28
from ganeti import errors
29
from ganeti import qlang
30
from ganeti import query
31

    
32
import testutils
33

    
34

    
35
class TestMakeSimpleFilter(unittest.TestCase):
36
  def _Test(self, field, names, expected, parse_exp=None):
37
    if parse_exp is None:
38
      parse_exp = names
39

    
40
    qfilter = qlang.MakeSimpleFilter(field, names)
41
    self.assertEqual(qfilter, expected)
42

    
43
  def test(self):
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"]])
50

    
51

    
52
class TestParseFilter(unittest.TestCase):
53
  def setUp(self):
54
    self.parser = qlang.BuildFilterParser()
55

    
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)
59

    
60
  def test(self):
61
    self._Test("name==\"foobar\"", [qlang.OP_EQUAL, "name", "foobar"])
62
    self._Test("name=='foobar'", [qlang.OP_EQUAL, "name", "foobar"])
63

    
64
    self._Test("valA==1 and valB==2 or valC==3",
65
               [qlang.OP_OR,
66
                [qlang.OP_AND, [qlang.OP_EQUAL, "valA", 1],
67
                               [qlang.OP_EQUAL, "valB", 2]],
68
                [qlang.OP_EQUAL, "valC", 3]])
69

    
70
    self._Test(("(name\n==\"foobar\") and (xyz==\"va)ue\" and k == 256 or"
71
                " x ==\t\"y\"\n) and mc"),
72
               [qlang.OP_AND,
73
                [qlang.OP_EQUAL, "name", "foobar"],
74
                [qlang.OP_OR,
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"]])
79

    
80
    self._Test("(xyz==\"v\" or k == 256 and x == \"y\")",
81
               [qlang.OP_OR,
82
                [qlang.OP_EQUAL, "xyz", "v"],
83
                [qlang.OP_AND, [qlang.OP_EQUAL, "k", 256],
84
                               [qlang.OP_EQUAL, "x", "y"]]])
85

    
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"],
96
               expect_filter=False)
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],
100
                 expect_filter=False)
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)",
111
               [qlang.OP_NOT,
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"]]])
120

    
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.*|",
125
               [qlang.OP_OR,
126
                [qlang.OP_AND,
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))])
133

    
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"])
143

    
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.*")]])
149

    
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])
154

    
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"])
158

    
159
  def testError(self):
160
    # Invalid field names, meaning no boolean check is done
161
    tests = ["#invalid!filter#", "m/x/,"]
162

    
163
    # Unknown regexp flag
164
    tests.append("name=~m#a#g")
165

    
166
    # Incomplete regexp group
167
    tests.append("name=~^[^")
168

    
169
    # Valid flag, but in uppercase
170
    tests.append("asdf =~ m|abc|I")
171

    
172
    # Non-matching regexp delimiters
173
    tests.append("name =~ /foobarbaz#")
174

    
175
    # Invalid operators
176
    tests.append("name <> value")
177
    tests.append("name => value")
178
    tests.append("name =< value")
179

    
180
    for qfilter in tests:
181
      try:
182
        qlang.ParseFilter(qfilter, parser=self.parser)
183
      except errors.QueryFilterParseError, err:
184
        self.assertEqual(len(err.GetDetails()), 3)
185
      else:
186
        self.fail("Invalid filter '%s' did not raise exception" % qfilter)
187

    
188

    
189
class TestMakeFilter(unittest.TestCase):
190
  def testNoNames(self):
191
    self.assertEqual(qlang.MakeFilter([], False), None)
192
    self.assertEqual(qlang.MakeFilter(None, False), None)
193

    
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"]])
198

    
199
  def testPlainNamesOtherNamefield(self):
200
    self.assertEqual(qlang.MakeFilter(["mailA", "mailB"], False,
201
                                      namefield="id"),
202
                     [qlang.OP_OR, [qlang.OP_EQUAL, "id", "mailA"],
203
                                   [qlang.OP_EQUAL, "id", "mailB"]])
204

    
205
  def testForcedFilter(self):
206
    for i in [None, [], ["1", "2"], ["", "", ""], ["a", "b", "c", "d"]]:
207
      self.assertRaises(errors.OpPrereqError, qlang.MakeFilter, i, True)
208

    
209
    # Glob pattern shouldn't parse as filter
210
    self.assertRaises(errors.QueryFilterParseError,
211
                      qlang.MakeFilter, ["*.site"], True)
212

    
213
    # Plain name parses as boolean filter
214
    self.assertEqual(qlang.MakeFilter(["web1"], True), [qlang.OP_TRUE, "web1"])
215

    
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")])
224

    
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]])
230

    
231
    # Invalid filters
232
    for i in ["foo==bar", "foo+=1"]:
233
      self.assertRaises(errors.QueryFilterParseError,
234
                        qlang.MakeFilter, [i], False)
235

    
236
  def testGlob(self):
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),
244
                     [qlang.OP_OR,
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")]])
251

    
252

    
253
if __name__ == "__main__":
254
  testutils.GanetiTestProgram()