Update default instance kernel version
[ganeti-local] / test / ganeti.query_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2010, 2011, 2012 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.query"""
23
24 import re
25 import unittest
26 import random
27
28 from ganeti import constants
29 from ganeti import utils
30 from ganeti import compat
31 from ganeti import errors
32 from ganeti import query
33 from ganeti import objects
34 from ganeti import cmdlib
35
36 import testutils
37
38
39 class TestConstants(unittest.TestCase):
40   def test(self):
41     self.assertEqual(set(query._VERIFY_FN.keys()),
42                      constants.QFT_ALL)
43
44
45 class _QueryData:
46   def __init__(self, data, **kwargs):
47     self.data = data
48
49     for name, value in kwargs.items():
50       setattr(self, name, value)
51
52   def __iter__(self):
53     return iter(self.data)
54
55
56 def _GetDiskSize(nr, ctx, item):
57   disks = item["disks"]
58   try:
59     return disks[nr]
60   except IndexError:
61     return query._FS_UNAVAIL
62
63
64 class TestQuery(unittest.TestCase):
65   def test(self):
66     (STATIC, DISK) = range(10, 12)
67
68     fielddef = query._PrepareFieldList([
69       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
70        STATIC, 0, lambda ctx, item: item["name"]),
71       (query._MakeField("master", "Master", constants.QFT_BOOL, "Master"),
72        STATIC, 0, lambda ctx, item: ctx.mastername == item["name"]),
73       ] +
74       [(query._MakeField("disk%s.size" % i, "DiskSize%s" % i,
75                          constants.QFT_UNIT, "Disk size %s" % i),
76         DISK, 0, compat.partial(_GetDiskSize, i))
77        for i in range(4)], [])
78
79     q = query.Query(fielddef, ["name"])
80     self.assertEqual(q.RequestedData(), set([STATIC]))
81     self.assertEqual(len(q._fields), 1)
82     self.assertEqual(len(q.GetFields()), 1)
83     self.assertEqual(q.GetFields()[0].ToDict(),
84       objects.QueryFieldDefinition(name="name",
85                                    title="Name",
86                                    kind=constants.QFT_TEXT,
87                                    doc="Name").ToDict())
88
89     # Create data only once query has been prepared
90     data = [
91       { "name": "node1", "disks": [0, 1, 2], },
92       { "name": "node2", "disks": [3, 4], },
93       { "name": "node3", "disks": [5, 6, 7], },
94       ]
95
96     self.assertEqual(q.Query(_QueryData(data, mastername="node3")),
97                      [[(constants.RS_NORMAL, "node1")],
98                       [(constants.RS_NORMAL, "node2")],
99                       [(constants.RS_NORMAL, "node3")]])
100     self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node3")),
101                      [["node1"], ["node2"], ["node3"]])
102
103     q = query.Query(fielddef, ["name", "master"])
104     self.assertEqual(q.RequestedData(), set([STATIC]))
105     self.assertEqual(len(q._fields), 2)
106     self.assertEqual(q.Query(_QueryData(data, mastername="node3")),
107                      [[(constants.RS_NORMAL, "node1"),
108                        (constants.RS_NORMAL, False)],
109                       [(constants.RS_NORMAL, "node2"),
110                        (constants.RS_NORMAL, False)],
111                       [(constants.RS_NORMAL, "node3"),
112                        (constants.RS_NORMAL, True)],
113                      ])
114
115     q = query.Query(fielddef, ["name", "master", "disk0.size"])
116     self.assertEqual(q.RequestedData(), set([STATIC, DISK]))
117     self.assertEqual(len(q._fields), 3)
118     self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
119                      [[(constants.RS_NORMAL, "node1"),
120                        (constants.RS_NORMAL, False),
121                        (constants.RS_NORMAL, 0)],
122                       [(constants.RS_NORMAL, "node2"),
123                        (constants.RS_NORMAL, True),
124                        (constants.RS_NORMAL, 3)],
125                       [(constants.RS_NORMAL, "node3"),
126                        (constants.RS_NORMAL, False),
127                        (constants.RS_NORMAL, 5)],
128                      ])
129
130     # With unknown column
131     q = query.Query(fielddef, ["disk2.size", "disk1.size", "disk99.size",
132                                "disk0.size"])
133     self.assertEqual(q.RequestedData(), set([DISK]))
134     self.assertEqual(len(q._fields), 4)
135     self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
136                      [[(constants.RS_NORMAL, 2),
137                        (constants.RS_NORMAL, 1),
138                        (constants.RS_UNKNOWN, None),
139                        (constants.RS_NORMAL, 0)],
140                       [(constants.RS_UNAVAIL, None),
141                        (constants.RS_NORMAL, 4),
142                        (constants.RS_UNKNOWN, None),
143                        (constants.RS_NORMAL, 3)],
144                       [(constants.RS_NORMAL, 7),
145                        (constants.RS_NORMAL, 6),
146                        (constants.RS_UNKNOWN, None),
147                        (constants.RS_NORMAL, 5)],
148                      ])
149     self.assertRaises(errors.OpPrereqError, q.OldStyleQuery,
150                       _QueryData(data, mastername="node2"))
151     self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], [
152                      { "name": "disk2.size", "title": "DiskSize2",
153                        "kind": constants.QFT_UNIT, "doc": "Disk size 2", },
154                      { "name": "disk1.size", "title": "DiskSize1",
155                        "kind": constants.QFT_UNIT, "doc": "Disk size 1", },
156                      { "name": "disk99.size", "title": "disk99.size",
157                        "kind": constants.QFT_UNKNOWN,
158                        "doc": "Unknown field 'disk99.size'", },
159                      { "name": "disk0.size", "title": "DiskSize0",
160                        "kind": constants.QFT_UNIT, "doc": "Disk size 0", },
161                      ])
162
163     # Empty query
164     q = query.Query(fielddef, [])
165     self.assertEqual(q.RequestedData(), set([]))
166     self.assertEqual(len(q._fields), 0)
167     self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
168                      [[], [], []])
169     self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node2")),
170                      [[], [], []])
171     self.assertEqual(q.GetFields(), [])
172
173   def testPrepareFieldList(self):
174     # Duplicate titles
175     for (a, b) in [("name", "name"), ("NAME", "name")]:
176       self.assertRaises(AssertionError, query._PrepareFieldList, [
177         (query._MakeField("name", b, constants.QFT_TEXT, "Name"), None, 0,
178          lambda *args: None),
179         (query._MakeField("other", a, constants.QFT_TEXT, "Other"), None, 0,
180          lambda *args: None),
181         ], [])
182
183     # Non-lowercase names
184     self.assertRaises(AssertionError, query._PrepareFieldList, [
185       (query._MakeField("NAME", "Name", constants.QFT_TEXT, "Name"), None, 0,
186        lambda *args: None),
187       ], [])
188     self.assertRaises(AssertionError, query._PrepareFieldList, [
189       (query._MakeField("Name", "Name", constants.QFT_TEXT, "Name"), None, 0,
190        lambda *args: None),
191       ], [])
192
193     # Empty name
194     self.assertRaises(AssertionError, query._PrepareFieldList, [
195       (query._MakeField("", "Name", constants.QFT_TEXT, "Name"), None, 0,
196        lambda *args: None),
197       ], [])
198
199     # Empty title
200     self.assertRaises(AssertionError, query._PrepareFieldList, [
201       (query._MakeField("name", "", constants.QFT_TEXT, "Name"), None, 0,
202        lambda *args: None),
203       ], [])
204
205     # Whitespace in title
206     self.assertRaises(AssertionError, query._PrepareFieldList, [
207       (query._MakeField("name", "Co lu mn", constants.QFT_TEXT, "Name"),
208        None, 0, lambda *args: None),
209       ], [])
210
211     # No callable function
212     self.assertRaises(AssertionError, query._PrepareFieldList, [
213       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
214        None, 0, None),
215       ], [])
216
217     # Invalid documentation
218     for doc in ["", ".", "Hello world\n", "Hello\nWo\nrld", "Hello World!",
219                 "HelloWorld.", "only lowercase", ",", " x y z .\t", "  "]:
220       self.assertRaises(AssertionError, query._PrepareFieldList, [
221         (query._MakeField("name", "Name", constants.QFT_TEXT, doc),
222         None, 0, lambda *args: None),
223         ], [])
224
225     # Duplicate field name
226     self.assertRaises(ValueError, query._PrepareFieldList, [
227       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
228        None, 0, lambda *args: None),
229       (query._MakeField("name", "Other", constants.QFT_OTHER, "Other"),
230        None, 0, lambda *args: None),
231       ], [])
232
233   def testUnknown(self):
234     fielddef = query._PrepareFieldList([
235       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
236        None, 0, lambda _, item: "name%s" % item),
237       (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP, "Other"),
238        None, 0, lambda *args: 1234),
239       (query._MakeField("nodata", "NoData", constants.QFT_NUMBER, "No data"),
240        None, 0, lambda *args: query._FS_NODATA ),
241       (query._MakeField("unavail", "Unavail", constants.QFT_BOOL, "Unavail"),
242        None, 0, lambda *args: query._FS_UNAVAIL),
243       ], [])
244
245     for selected in [["foo"], ["Hello", "World"],
246                      ["name1", "other", "foo"]]:
247       q = query.Query(fielddef, selected)
248       self.assertEqual(len(q._fields), len(selected))
249       self.assert_(compat.all(len(row) == len(selected)
250                               for row in q.Query(_QueryData(range(1, 10)))))
251       self.assertEqual(q.Query(_QueryData(range(1, 10))),
252                        [[(constants.RS_UNKNOWN, None)] * len(selected)
253                         for i in range(1, 10)])
254       self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
255                        [{ "name": name, "title": name,
256                           "kind": constants.QFT_UNKNOWN,
257                           "doc": "Unknown field '%s'" % name}
258                         for name in selected])
259
260     q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
261     self.assertEqual(len(q._fields), 4)
262     self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
263                      ["name%s" % i, 1234, None, None]
264                      for i in range(1, 10)
265                      ])
266
267     q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
268     self.assertEqual(len(q._fields), 5)
269     self.assertEqual(q.Query(_QueryData(range(1, 10))),
270                      [[(constants.RS_NORMAL, "name%s" % i),
271                        (constants.RS_NORMAL, 1234),
272                        (constants.RS_NODATA, None),
273                        (constants.RS_UNAVAIL, None),
274                        (constants.RS_UNKNOWN, None)]
275                       for i in range(1, 10)])
276
277   def testAliases(self):
278     fields = [
279       (query._MakeField("a", "a-title", constants.QFT_TEXT, "Field A"),
280        None, 0, lambda *args: None),
281       (query._MakeField("b", "b-title", constants.QFT_TEXT, "Field B"),
282        None, 0, lambda *args: None),
283       ]
284     # duplicate field
285     self.assertRaises(AssertionError, query._PrepareFieldList, fields,
286                       [("b", "a")])
287     self.assertRaises(AssertionError, query._PrepareFieldList, fields,
288                       [("c", "b"), ("c", "a")])
289     # missing target
290     self.assertRaises(AssertionError, query._PrepareFieldList, fields,
291                       [("c", "d")])
292     fdefs = query._PrepareFieldList(fields, [("c", "b")])
293     self.assertEqual(len(fdefs), 3)
294     self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
295
296
297 class TestGetNodeRole(unittest.TestCase):
298   def test(self):
299     tested_role = set()
300
301     checks = [
302       (constants.NR_MASTER, "node1", objects.Node(name="node1")),
303       (constants.NR_MCANDIDATE, "master",
304        objects.Node(name="node1", master_candidate=True)),
305       (constants.NR_REGULAR, "master", objects.Node(name="node1")),
306       (constants.NR_DRAINED, "master",
307        objects.Node(name="node1", drained=True)),
308       (constants.NR_OFFLINE,
309        "master", objects.Node(name="node1", offline=True)),
310       ]
311
312     for (role, master_name, node) in checks:
313       result = query._GetNodeRole(node, master_name)
314       self.assertEqual(result, role)
315       tested_role.add(result)
316
317     self.assertEqual(tested_role, constants.NR_ALL)
318
319
320 class TestNodeQuery(unittest.TestCase):
321   def _Create(self, selected):
322     return query.Query(query.NODE_FIELDS, selected)
323
324   def testSimple(self):
325     cluster = objects.Cluster(cluster_name="testcluster",
326                               ndparams=constants.NDC_DEFAULTS.copy())
327     grp1 = objects.NodeGroup(name="default",
328                              uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
329                              alloc_policy=constants.ALLOC_POLICY_PREFERRED,
330                              ipolicy=objects.MakeEmptyIPolicy(),
331                              ndparams={},
332                              )
333     grp2 = objects.NodeGroup(name="group2",
334                              uuid="c0e89160-18e7-11e0-a46e-001d0904babe",
335                              alloc_policy=constants.ALLOC_POLICY_PREFERRED,
336                              ipolicy=objects.MakeEmptyIPolicy(),
337                              ndparams={constants.ND_SPINDLE_COUNT: 2},
338                              )
339     groups = {grp1.uuid: grp1, grp2.uuid: grp2}
340     nodes = [
341       objects.Node(name="node1", drained=False, group=grp1.uuid, ndparams={}),
342       objects.Node(name="node2", drained=True, group=grp2.uuid, ndparams={}),
343       objects.Node(name="node3", drained=False, group=grp1.uuid,
344                    ndparams={constants.ND_SPINDLE_COUNT: 4}),
345       ]
346     for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
347       nqd = query.NodeQueryData(nodes, live_data, None, None, None,
348                                 groups, None, cluster)
349
350       q = self._Create(["name", "drained"])
351       self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
352       self.assertEqual(q.Query(nqd),
353                        [[(constants.RS_NORMAL, "node1"),
354                          (constants.RS_NORMAL, False)],
355                         [(constants.RS_NORMAL, "node2"),
356                          (constants.RS_NORMAL, True)],
357                         [(constants.RS_NORMAL, "node3"),
358                          (constants.RS_NORMAL, False)],
359                        ])
360       self.assertEqual(q.OldStyleQuery(nqd),
361                        [["node1", False],
362                         ["node2", True],
363                         ["node3", False]])
364       q = self._Create(["ndp/spindle_count"])
365       self.assertEqual(q.RequestedData(), set([query.NQ_GROUP]))
366       self.assertEqual(q.Query(nqd),
367                        [[(constants.RS_NORMAL,
368                           constants.NDC_DEFAULTS[constants.ND_SPINDLE_COUNT])],
369                         [(constants.RS_NORMAL,
370                           grp2.ndparams[constants.ND_SPINDLE_COUNT])],
371                         [(constants.RS_NORMAL,
372                           nodes[2].ndparams[constants.ND_SPINDLE_COUNT])],
373                        ])
374
375   def test(self):
376     selected = query.NODE_FIELDS.keys()
377     field_index = dict((field, idx) for idx, field in enumerate(selected))
378
379     q = self._Create(selected)
380     self.assertEqual(q.RequestedData(),
381                      set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
382                           query.NQ_GROUP, query.NQ_OOB]))
383
384     cluster = objects.Cluster(cluster_name="testcluster",
385       hvparams=constants.HVC_DEFAULTS,
386       beparams={
387         constants.PP_DEFAULT: constants.BEC_DEFAULTS,
388         },
389       nicparams={
390         constants.PP_DEFAULT: constants.NICC_DEFAULTS,
391         },
392       ndparams=constants.NDC_DEFAULTS,
393         )
394
395     node_names = ["node%s" % i for i in range(20)]
396     master_name = node_names[3]
397     nodes = [
398       objects.Node(name=name,
399                    primary_ip="192.0.2.%s" % idx,
400                    secondary_ip="192.0.100.%s" % idx,
401                    serial_no=7789 * idx,
402                    master_candidate=(name != master_name and idx % 3 == 0),
403                    offline=False,
404                    drained=False,
405                    powered=True,
406                    vm_capable=True,
407                    master_capable=False,
408                    ndparams={},
409                    group="default",
410                    ctime=1290006900,
411                    mtime=1290006913,
412                    uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
413       for idx, name in enumerate(node_names)
414       ]
415
416     master_node = nodes[3]
417     master_node.AddTag("masternode")
418     master_node.AddTag("another")
419     master_node.AddTag("tag")
420     master_node.ctime = None
421     master_node.mtime = None
422     assert master_node.name == master_name
423
424     live_data_name = node_names[4]
425     assert live_data_name != master_name
426
427     fake_live_data = {
428       "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
429       "cnodes": 4,
430       "csockets": 4,
431       "ctotal": 8,
432       "mnode": 128,
433       "mfree": 100,
434       "mtotal": 4096,
435       "dfree": 5 * 1024 * 1024,
436       "dtotal": 100 * 1024 * 1024,
437       }
438
439     assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
440             sorted(fake_live_data.keys()))
441
442     live_data = dict.fromkeys(node_names, {})
443     live_data[live_data_name] = \
444       dict((query._NODE_LIVE_FIELDS[name][2], value)
445            for name, value in fake_live_data.items())
446
447     node_to_primary = dict((name, set()) for name in node_names)
448     node_to_primary[master_name].update(["inst1", "inst2"])
449
450     node_to_secondary = dict((name, set()) for name in node_names)
451     node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
452
453     ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
454     groups = {
455       ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
456       }
457
458     oob_not_powered_node = node_names[0]
459     nodes[0].powered = False
460     oob_support = dict((name, False) for name in node_names)
461     oob_support[master_name] = True
462     oob_support[oob_not_powered_node] = True
463
464     master_node.group = ng_uuid
465
466     nqd = query.NodeQueryData(nodes, live_data, master_name,
467                               node_to_primary, node_to_secondary, groups,
468                               oob_support, cluster)
469     result = q.Query(nqd)
470     self.assert_(compat.all(len(row) == len(selected) for row in result))
471     self.assertEqual([row[field_index["name"]] for row in result],
472                      [(constants.RS_NORMAL, name) for name in node_names])
473
474     node_to_row = dict((row[field_index["name"]][1], idx)
475                        for idx, row in enumerate(result))
476
477     master_row = result[node_to_row[master_name]]
478     self.assert_(master_row[field_index["master"]])
479     self.assert_(master_row[field_index["role"]], "M")
480     self.assertEqual(master_row[field_index["group"]],
481                      (constants.RS_NORMAL, "ng1"))
482     self.assertEqual(master_row[field_index["group.uuid"]],
483                      (constants.RS_NORMAL, ng_uuid))
484     self.assertEqual(master_row[field_index["ctime"]],
485                      (constants.RS_UNAVAIL, None))
486     self.assertEqual(master_row[field_index["mtime"]],
487                      (constants.RS_UNAVAIL, None))
488
489     self.assert_(row[field_index["pip"]] == node.primary_ip and
490                  row[field_index["sip"]] == node.secondary_ip and
491                  set(row[field_index["tags"]]) == node.GetTags() and
492                  row[field_index["serial_no"]] == node.serial_no and
493                  row[field_index["role"]] == query._GetNodeRole(node,
494                                                                 master_name) and
495                  (node.name == master_name or
496                   (row[field_index["group"]] == "<unknown>" and
497                    row[field_index["group.uuid"]] is None and
498                    row[field_index["ctime"]] == (constants.RS_NORMAL,
499                                                  node.ctime) and
500                    row[field_index["mtime"]] == (constants.RS_NORMAL,
501                                                  node.mtime) and
502                    row[field_index["powered"]] == (constants.RS_NORMAL,
503                                                    True))) or
504                  (node.name == oob_not_powered_node and
505                   row[field_index["powered"]] == (constants.RS_NORMAL,
506                                                   False)) or
507                  row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
508                  for row, node in zip(result, nodes))
509
510     live_data_row = result[node_to_row[live_data_name]]
511
512     for (field, value) in fake_live_data.items():
513       self.assertEqual(live_data_row[field_index[field]],
514                        (constants.RS_NORMAL, value))
515
516     self.assertEqual(master_row[field_index["pinst_cnt"]],
517                      (constants.RS_NORMAL, 2))
518     self.assertEqual(live_data_row[field_index["sinst_cnt"]],
519                      (constants.RS_NORMAL, 3))
520     self.assertEqual(master_row[field_index["pinst_list"]],
521                      (constants.RS_NORMAL,
522                       list(node_to_primary[master_name])))
523     self.assertEqual(live_data_row[field_index["sinst_list"]],
524                      (constants.RS_NORMAL,
525                       list(node_to_secondary[live_data_name])))
526
527   def testGetLiveNodeField(self):
528     nodes = [
529       objects.Node(name="node1", drained=False, offline=False,
530                    vm_capable=True),
531       objects.Node(name="node2", drained=True, offline=False,
532                    vm_capable=True),
533       objects.Node(name="node3", drained=False, offline=False,
534                    vm_capable=True),
535       objects.Node(name="node4", drained=False, offline=True,
536                    vm_capable=True),
537       objects.Node(name="node5", drained=False, offline=False,
538                    vm_capable=False),
539       ]
540     live_data = dict.fromkeys([node.name for node in nodes], {})
541
542     # No data
543     nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
544     self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
545                                              nqd, nodes[0]),
546                      query._FS_NODATA)
547
548     # Missing field
549     ctx = _QueryData(None, curlive_data={
550       "some": 1,
551       "other": 2,
552       })
553     self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
554                                              ctx, nodes[0]),
555                      query._FS_UNAVAIL)
556
557     # Wrong format/datatype
558     ctx = _QueryData(None, curlive_data={
559       "hello": ["Hello World"],
560       "other": 2,
561       })
562     self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
563                                              ctx, nodes[0]),
564                      query._FS_UNAVAIL)
565
566     # Offline node
567     assert nodes[3].offline
568     ctx = _QueryData(None, curlive_data={})
569     self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
570                                              ctx, nodes[3]),
571                      query._FS_OFFLINE, None)
572
573     # Wrong field type
574     ctx = _QueryData(None, curlive_data={"hello": 123})
575     self.assertRaises(AssertionError, query._GetLiveNodeField,
576                       "hello", constants.QFT_BOOL, ctx, nodes[0])
577
578     # Non-vm_capable node
579     assert not nodes[4].vm_capable
580     ctx = _QueryData(None, curlive_data={})
581     self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
582                                              ctx, nodes[4]),
583                      query._FS_UNAVAIL, None)
584
585
586 class TestInstanceQuery(unittest.TestCase):
587   def _Create(self, selected):
588     return query.Query(query.INSTANCE_FIELDS, selected)
589
590   def testSimple(self):
591     q = self._Create(["name", "be/maxmem", "ip"])
592     self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
593
594     cluster = objects.Cluster(cluster_name="testcluster",
595       hvparams=constants.HVC_DEFAULTS,
596       beparams={
597         constants.PP_DEFAULT: constants.BEC_DEFAULTS,
598         },
599       nicparams={
600         constants.PP_DEFAULT: constants.NICC_DEFAULTS,
601         },
602       os_hvp={},
603       osparams={})
604
605     instances = [
606       objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
607                        nics=[], os="deb1"),
608       objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
609         os="foomoo",
610         beparams={
611           constants.BE_MAXMEM: 512,
612         }),
613       objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
614         os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
615       ]
616
617     iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
618                                   set(), {}, None, None)
619     self.assertEqual(q.Query(iqd),
620       [[(constants.RS_NORMAL, "inst1"),
621         (constants.RS_NORMAL, 128),
622         (constants.RS_UNAVAIL, None),
623        ],
624        [(constants.RS_NORMAL, "inst2"),
625         (constants.RS_NORMAL, 512),
626         (constants.RS_UNAVAIL, None),
627        ],
628        [(constants.RS_NORMAL, "inst3"),
629         (constants.RS_NORMAL, 128),
630         (constants.RS_NORMAL, "192.0.2.99"),
631        ]])
632     self.assertEqual(q.OldStyleQuery(iqd),
633       [["inst1", 128, None],
634        ["inst2", 512, None],
635        ["inst3", 128, "192.0.2.99"]])
636
637   def test(self):
638     selected = query.INSTANCE_FIELDS.keys()
639     fieldidx = dict((field, idx) for idx, field in enumerate(selected))
640
641     macs = ["00:11:22:%02x:%02x:%02x" % (i % 255, i % 3, (i * 123) % 255)
642             for i in range(20)]
643
644     q = self._Create(selected)
645     self.assertEqual(q.RequestedData(),
646                      set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
647                           query.IQ_CONSOLE, query.IQ_NODES]))
648
649     cluster = objects.Cluster(cluster_name="testcluster",
650       hvparams=constants.HVC_DEFAULTS,
651       beparams={
652         constants.PP_DEFAULT: constants.BEC_DEFAULTS,
653         },
654       nicparams={
655         constants.PP_DEFAULT: constants.NICC_DEFAULTS,
656         },
657       os_hvp={},
658       tcpudp_port_pool=set(),
659       osparams={
660         "deb99": {
661           "clean_install": "yes",
662           },
663         })
664
665     offline_nodes = ["nodeoff1", "nodeoff2"]
666     bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
667     nodes = ["node%s" % i for i in range(10)] + bad_nodes
668
669     instances = [
670       objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
671         uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
672         ctime=1291244000, mtime=1291244400, serial_no=30,
673         admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_PVM,
674         os="linux1",
675         primary_node="node1",
676         disk_template=constants.DT_PLAIN,
677         disks=[],
678         osparams={}),
679       objects.Instance(name="inst2", hvparams={}, nics=[],
680         uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
681         ctime=1291211000, mtime=1291211077, serial_no=1,
682         admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
683         os="deb99",
684         primary_node="node5",
685         disk_template=constants.DT_DISKLESS,
686         disks=[],
687         beparams={
688           constants.BE_MAXMEM: 512,
689           constants.BE_MINMEM: 256,
690         },
691         osparams={}),
692       objects.Instance(name="inst3", hvparams={}, beparams={},
693         uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
694         ctime=1291011000, mtime=1291013000, serial_no=1923,
695         admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_KVM,
696         os="busybox",
697         primary_node="node6",
698         disk_template=constants.DT_DRBD8,
699         disks=[],
700         nics=[
701           objects.NIC(ip="192.0.2.99", mac=macs.pop(),
702                       nicparams={
703                         constants.NIC_LINK: constants.DEFAULT_BRIDGE,
704                         }),
705           objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
706           ],
707         osparams={}),
708       objects.Instance(name="inst4", hvparams={}, beparams={},
709         uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
710         ctime=1291244390, mtime=1291244395, serial_no=25,
711         admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_PVM,
712         os="linux1",
713         primary_node="nodeoff2",
714         disk_template=constants.DT_DRBD8,
715         disks=[],
716         nics=[
717           objects.NIC(ip="192.0.2.1", mac=macs.pop(),
718                       nicparams={
719                         constants.NIC_LINK: constants.DEFAULT_BRIDGE,
720                         }),
721           objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
722           objects.NIC(ip="192.0.2.3", mac=macs.pop(),
723                       nicparams={
724                         constants.NIC_MODE: constants.NIC_MODE_ROUTED,
725                         }),
726           objects.NIC(ip="192.0.2.4", mac=macs.pop(),
727                       nicparams={
728                         constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
729                         constants.NIC_LINK: "eth123",
730                         }),
731           ],
732         osparams={}),
733       objects.Instance(name="inst5", hvparams={}, nics=[],
734         uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
735         ctime=1231211000, mtime=1261200000, serial_no=3,
736         admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
737         os="deb99",
738         primary_node="nodebad2",
739         disk_template=constants.DT_DISKLESS,
740         disks=[],
741         beparams={
742           constants.BE_MAXMEM: 512,
743           constants.BE_MINMEM: 512,
744         },
745         osparams={}),
746       objects.Instance(name="inst6", hvparams={}, nics=[],
747         uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
748         ctime=7513, mtime=11501, serial_no=13390,
749         admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
750         os="deb99",
751         primary_node="node7",
752         disk_template=constants.DT_DISKLESS,
753         disks=[],
754         beparams={
755           constants.BE_MAXMEM: 768,
756           constants.BE_MINMEM: 256,
757         },
758         osparams={
759           "clean_install": "no",
760           }),
761       objects.Instance(name="inst7", hvparams={}, nics=[],
762         uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
763         ctime=None, mtime=None, serial_no=1947,
764         admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
765         os="deb99",
766         primary_node="node6",
767         disk_template=constants.DT_DISKLESS,
768         disks=[],
769         beparams={},
770         osparams={}),
771       objects.Instance(name="inst8", hvparams={}, nics=[],
772         uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920f",
773         ctime=None, mtime=None, serial_no=19478,
774         admin_state=constants.ADMINST_OFFLINE, hypervisor=constants.HT_XEN_HVM,
775         os="deb99",
776         primary_node="node6",
777         disk_template=constants.DT_DISKLESS,
778         disks=[],
779         beparams={},
780         osparams={}),
781       ]
782
783     assert not utils.FindDuplicates(inst.name for inst in instances)
784
785     instbyname = dict((inst.name, inst) for inst in instances)
786
787     disk_usage = dict((inst.name,
788                        cmdlib._ComputeDiskSize(inst.disk_template,
789                                                [{"size": disk.size}
790                                                 for disk in inst.disks]))
791                       for inst in instances)
792
793     inst_bridges = {
794       "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
795       "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
796                 None, "eth123"],
797       }
798
799     live_data = {
800       "inst2": {
801         "vcpus": 3,
802         },
803       "inst4": {
804         "memory": 123,
805         },
806       "inst6": {
807         "memory": 768,
808         },
809       "inst7": {
810         "vcpus": 3,
811         },
812       }
813     wrongnode_inst = set(["inst7"])
814
815     consinfo = dict((inst.name, None) for inst in instances)
816     consinfo["inst7"] = \
817       objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
818                               host=instbyname["inst7"].primary_node,
819                               user=constants.GANETI_RUNAS,
820                               command=["hostname"]).ToDict()
821
822     iqd = query.InstanceQueryData(instances, cluster, disk_usage,
823                                   offline_nodes, bad_nodes, live_data,
824                                   wrongnode_inst, consinfo, {}, {})
825     result = q.Query(iqd)
826     self.assertEqual(len(result), len(instances))
827     self.assert_(compat.all(len(row) == len(selected)
828                             for row in result))
829
830     assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
831            "Offline nodes not included in bad nodes"
832
833     tested_status = set()
834
835     for (inst, row) in zip(instances, result):
836       assert inst.primary_node in nodes
837
838       self.assertEqual(row[fieldidx["name"]],
839                        (constants.RS_NORMAL, inst.name))
840
841       if inst.primary_node in offline_nodes:
842         exp_status = constants.INSTST_NODEOFFLINE
843       elif inst.primary_node in bad_nodes:
844         exp_status = constants.INSTST_NODEDOWN
845       elif inst.name in live_data:
846         if inst.name in wrongnode_inst:
847           exp_status = constants.INSTST_WRONGNODE
848         elif inst.admin_state == constants.ADMINST_UP:
849           exp_status = constants.INSTST_RUNNING
850         else:
851           exp_status = constants.INSTST_ERRORUP
852       elif inst.admin_state == constants.ADMINST_UP:
853         exp_status = constants.INSTST_ERRORDOWN
854       elif inst.admin_state == constants.ADMINST_DOWN:
855         exp_status = constants.INSTST_ADMINDOWN
856       else:
857         exp_status = constants.INSTST_ADMINOFFLINE
858
859       self.assertEqual(row[fieldidx["status"]],
860                        (constants.RS_NORMAL, exp_status))
861
862       (_, status) = row[fieldidx["status"]]
863       tested_status.add(status)
864
865       #FIXME(dynmem): check oper_ram vs min/max mem
866       for (field, livefield) in [("oper_vcpus", "vcpus")]:
867         if inst.primary_node in bad_nodes:
868           exp = (constants.RS_NODATA, None)
869         elif inst.name in live_data:
870           value = live_data[inst.name].get(livefield, None)
871           if value is None:
872             exp = (constants.RS_UNAVAIL, None)
873           else:
874             exp = (constants.RS_NORMAL, value)
875         else:
876           exp = (constants.RS_UNAVAIL, None)
877
878         self.assertEqual(row[fieldidx[field]], exp)
879
880       bridges = inst_bridges.get(inst.name, [])
881       self.assertEqual(row[fieldidx["nic.bridges"]],
882                        (constants.RS_NORMAL, bridges))
883       if bridges:
884         self.assertEqual(row[fieldidx["bridge"]],
885                          (constants.RS_NORMAL, bridges[0]))
886       else:
887         self.assertEqual(row[fieldidx["bridge"]],
888                          (constants.RS_UNAVAIL, None))
889
890       for i in range(constants.MAX_NICS):
891         if i < len(bridges) and bridges[i] is not None:
892           exp = (constants.RS_NORMAL, bridges[i])
893         else:
894           exp = (constants.RS_UNAVAIL, None)
895         self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
896
897       if inst.primary_node in bad_nodes:
898         exp = (constants.RS_NODATA, None)
899       else:
900         exp = (constants.RS_NORMAL, inst.name in live_data)
901       self.assertEqual(row[fieldidx["oper_state"]], exp)
902
903       cust_exp = (constants.RS_NORMAL, {})
904       if inst.os == "deb99":
905         if inst.name == "inst6":
906           exp = (constants.RS_NORMAL, {"clean_install": "no"})
907           cust_exp = exp
908         else:
909           exp = (constants.RS_NORMAL, {"clean_install": "yes"})
910       else:
911         exp = (constants.RS_NORMAL, {})
912       self.assertEqual(row[fieldidx["osparams"]], exp)
913       self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
914
915       usage = disk_usage[inst.name]
916       if usage is None:
917         usage = 0
918       self.assertEqual(row[fieldidx["disk_usage"]],
919                        (constants.RS_NORMAL, usage))
920
921       for alias, target in [("sda_size", "disk.size/0"),
922                             ("sdb_size", "disk.size/1"),
923                             ("vcpus", "be/vcpus"),
924                             ("ip", "nic.ip/0"),
925                             ("mac", "nic.mac/0"),
926                             ("bridge", "nic.bridge/0"),
927                             ("nic_mode", "nic.mode/0"),
928                             ("nic_link", "nic.link/0"),
929                             ]:
930         self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
931
932       for field in ["ctime", "mtime"]:
933         if getattr(inst, field) is None:
934           # No ctime/mtime
935           exp = (constants.RS_UNAVAIL, None)
936         else:
937           exp = (constants.RS_NORMAL, getattr(inst, field))
938         self.assertEqual(row[fieldidx[field]], exp)
939
940       self._CheckInstanceConsole(inst, row[fieldidx["console"]])
941
942     # Ensure all possible status' have been tested
943     self.assertEqual(tested_status, constants.INSTST_ALL)
944
945   def _CheckInstanceConsole(self, instance, (status, consdata)):
946     if instance.name == "inst7":
947       self.assertEqual(status, constants.RS_NORMAL)
948       console = objects.InstanceConsole.FromDict(consdata)
949       self.assertTrue(console.Validate())
950       self.assertEqual(console.host, instance.primary_node)
951     else:
952       self.assertEqual(status, constants.RS_UNAVAIL)
953
954
955 class TestGroupQuery(unittest.TestCase):
956
957   def setUp(self):
958     self.groups = [
959       objects.NodeGroup(name="default",
960                         uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
961                         alloc_policy=constants.ALLOC_POLICY_PREFERRED,
962                         ipolicy=objects.MakeEmptyIPolicy(),
963                         ndparams={},
964                         ),
965       objects.NodeGroup(name="restricted",
966                         uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
967                         alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
968                         ipolicy=objects.MakeEmptyIPolicy(),
969                         ndparams={}
970                         ),
971       ]
972     self.cluster = objects.Cluster(cluster_name="testcluster",
973       hvparams=constants.HVC_DEFAULTS,
974       beparams={
975         constants.PP_DEFAULT: constants.BEC_DEFAULTS,
976         },
977       nicparams={
978         constants.PP_DEFAULT: constants.NICC_DEFAULTS,
979         },
980       ndparams=constants.NDC_DEFAULTS,
981       ipolicy=constants.IPOLICY_DEFAULTS,
982       )
983
984   def _Create(self, selected):
985     return query.Query(query.GROUP_FIELDS, selected)
986
987   def testSimple(self):
988     q = self._Create(["name", "uuid", "alloc_policy"])
989     gqd = query.GroupQueryData(self.cluster, self.groups, None, None)
990
991     self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
992
993     self.assertEqual(q.Query(gqd),
994       [[(constants.RS_NORMAL, "default"),
995         (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
996         (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
997         ],
998        [(constants.RS_NORMAL, "restricted"),
999         (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1000         (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1001         ],
1002        ])
1003
1004   def testNodes(self):
1005     groups_to_nodes = {
1006       "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1007       "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1008       }
1009
1010     q = self._Create(["name", "node_cnt", "node_list"])
1011     gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None)
1012
1013     self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1014
1015     self.assertEqual(q.Query(gqd),
1016                      [[(constants.RS_NORMAL, "default"),
1017                        (constants.RS_NORMAL, 2),
1018                        (constants.RS_NORMAL, ["node1", "node2"]),
1019                        ],
1020                       [(constants.RS_NORMAL, "restricted"),
1021                        (constants.RS_NORMAL, 3),
1022                        (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1023                        ],
1024                       ])
1025
1026   def testInstances(self):
1027     groups_to_instances = {
1028       "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1029       "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1030       }
1031
1032     q = self._Create(["pinst_cnt", "pinst_list"])
1033     gqd = query.GroupQueryData(self.cluster, self.groups, None,
1034       groups_to_instances)
1035
1036     self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1037
1038     self.assertEqual(q.Query(gqd),
1039                      [[(constants.RS_NORMAL, 2),
1040                        (constants.RS_NORMAL, ["inst1", "inst2"]),
1041                        ],
1042                       [(constants.RS_NORMAL, 3),
1043                        (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1044                        ],
1045                       ])
1046
1047
1048 class TestOsQuery(unittest.TestCase):
1049   def _Create(self, selected):
1050     return query.Query(query.OS_FIELDS, selected)
1051
1052   def test(self):
1053     variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1054     api_versions = [10, 0, 15, 5]
1055     parameters = ["zpar3", "apar9"]
1056
1057     assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1058     assert (api_versions != sorted(api_versions) and
1059             api_versions != utils.NiceSort(variants))
1060     assert (parameters != sorted(parameters) and
1061             parameters != utils.NiceSort(parameters))
1062
1063     data = [
1064       query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1065                    variants=set(), api_versions=set(), parameters=set(),
1066                    node_status={ "some": "status", }),
1067       query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1068                    variants=set(variants),
1069                    api_versions=set(api_versions),
1070                    parameters=set(parameters),
1071                    node_status={ "some": "other", "status": None, }),
1072       ]
1073
1074
1075     q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1076                       "api_versions", "parameters", "node_status"])
1077     self.assertEqual(q.RequestedData(), set([]))
1078     self.assertEqual(q.Query(data),
1079                      [[(constants.RS_NORMAL, "debian"),
1080                        (constants.RS_NORMAL, False),
1081                        (constants.RS_NORMAL, False),
1082                        (constants.RS_NORMAL, False),
1083                        (constants.RS_NORMAL, []),
1084                        (constants.RS_NORMAL, []),
1085                        (constants.RS_NORMAL, []),
1086                        (constants.RS_NORMAL, {"some": "status"})],
1087                       [(constants.RS_NORMAL, "dos"),
1088                        (constants.RS_NORMAL, True),
1089                        (constants.RS_NORMAL, False),
1090                        (constants.RS_NORMAL, True),
1091                        (constants.RS_NORMAL,
1092                         ["plain", "v00", "v3", "v20", "v33", "var0"]),
1093                        (constants.RS_NORMAL, [0, 5, 10, 15]),
1094                        (constants.RS_NORMAL, ["apar9", "zpar3"]),
1095                        (constants.RS_NORMAL,
1096                         { "some": "other", "status": None, })
1097                        ]])
1098
1099
1100 class TestQueryFields(unittest.TestCase):
1101   def testAllFields(self):
1102     for fielddefs in query.ALL_FIELD_LISTS:
1103       result = query.QueryFields(fielddefs, None)
1104       self.assert_(isinstance(result, dict))
1105       response = objects.QueryFieldsResponse.FromDict(result)
1106       self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1107         [(fdef2.name, fdef2.title)
1108          for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1109                                                 key=lambda x: x[0].name)])
1110
1111   def testSomeFields(self):
1112     rnd = random.Random(5357)
1113
1114     for _ in range(10):
1115       for fielddefs in query.ALL_FIELD_LISTS:
1116         if len(fielddefs) > 20:
1117           sample_size = rnd.randint(5, 20)
1118         else:
1119           sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1120         fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1121                                                          sample_size)]
1122         result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1123         self.assert_(isinstance(result, dict))
1124         response = objects.QueryFieldsResponse.FromDict(result)
1125         self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1126                          [(fdef2.name, fdef2.title) for fdef2 in fields])
1127
1128
1129 class TestQueryFilter(unittest.TestCase):
1130   def testRequestedNames(self):
1131     innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1132
1133     for fielddefs in query.ALL_FIELD_LISTS:
1134       assert "name" in fielddefs
1135
1136       # No name field
1137       q = query.Query(fielddefs, ["name"], qfilter=["=", "name", "abc"],
1138                       namefield=None)
1139       self.assertEqual(q.RequestedNames(), None)
1140
1141       # No filter
1142       q = query.Query(fielddefs, ["name"], qfilter=None, namefield="name")
1143       self.assertEqual(q.RequestedNames(), None)
1144
1145       # Check empty query
1146       q = query.Query(fielddefs, ["name"], qfilter=["|"], namefield="name")
1147       self.assertEqual(q.RequestedNames(), None)
1148
1149       # Check order
1150       q = query.Query(fielddefs, ["name"], qfilter=["|"] + innerfilter,
1151                       namefield="name")
1152       self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1153
1154       # Check reverse order
1155       q = query.Query(fielddefs, ["name"],
1156                       qfilter=["|"] + list(reversed(innerfilter)),
1157                       namefield="name")
1158       self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1159
1160       # Duplicates
1161       q = query.Query(fielddefs, ["name"],
1162                       qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1163                       namefield="name")
1164       self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1165
1166       # Unknown name field
1167       self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1168                         namefield="_unknown_field_")
1169
1170       # Filter with AND
1171       q = query.Query(fielddefs, ["name"],
1172                       qfilter=["|", ["=", "name", "foo"],
1173                                     ["&", ["=", "name", ""]]],
1174                       namefield="name")
1175       self.assertTrue(q.RequestedNames() is None)
1176
1177       # Filter with NOT
1178       q = query.Query(fielddefs, ["name"],
1179                       qfilter=["|", ["=", "name", "foo"],
1180                                     ["!", ["=", "name", ""]]],
1181                       namefield="name")
1182       self.assertTrue(q.RequestedNames() is None)
1183
1184       # Filter with only OR (names must be in correct order)
1185       q = query.Query(fielddefs, ["name"],
1186                       qfilter=["|", ["=", "name", "x17361"],
1187                                     ["|", ["=", "name", "x22015"]],
1188                                     ["|", ["|", ["=", "name", "x13193"]]],
1189                                     ["=", "name", "x15215"]],
1190                       namefield="name")
1191       self.assertEqual(q.RequestedNames(),
1192                        ["x17361", "x22015", "x13193", "x15215"])
1193
1194   @staticmethod
1195   def _GenNestedFilter(op, depth):
1196     nested = ["=", "name", "value"]
1197     for i in range(depth):
1198       nested = [op, nested]
1199     return nested
1200
1201   def testCompileFilter(self):
1202     levels_max = query._FilterCompilerHelper._LEVELS_MAX
1203
1204     checks = [
1205       [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1206       ["=", "_unknown_field", "value"],
1207       self._GenNestedFilter("|", levels_max),
1208       self._GenNestedFilter("|", levels_max * 3),
1209       self._GenNestedFilter("!", levels_max),
1210       ]
1211
1212     for fielddefs in query.ALL_FIELD_LISTS:
1213       for qfilter in checks:
1214         self.assertRaises(errors.ParameterError, query._CompileFilter,
1215                           fielddefs, None, qfilter)
1216
1217       for op in ["|", "!"]:
1218         qfilter = self._GenNestedFilter(op, levels_max - 1)
1219         self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1220                                                       qfilter)))
1221
1222   def testQueryInputOrder(self):
1223     fielddefs = query._PrepareFieldList([
1224       (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1225        None, 0, lambda ctx, item: item["pnode"]),
1226       (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1227        None, 0, lambda ctx, item: item["snode"]),
1228       ], [])
1229
1230     data = [
1231       { "pnode": "node1", "snode": "node44", },
1232       { "pnode": "node30", "snode": "node90", },
1233       { "pnode": "node25", "snode": "node1", },
1234       { "pnode": "node20", "snode": "node1", },
1235       ]
1236
1237     qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1238
1239     q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1240                     qfilter=qfilter)
1241     self.assertTrue(q.RequestedNames() is None)
1242     self.assertFalse(q.RequestedData())
1243     self.assertEqual(q.Query(data),
1244       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1245        [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1246        [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1247
1248     # Try again with reversed input data
1249     self.assertEqual(q.Query(reversed(data)),
1250       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1251        [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1252        [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1253
1254     # No name field, result must be in incoming order
1255     q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1256                     qfilter=qfilter)
1257     self.assertFalse(q.RequestedData())
1258     self.assertEqual(q.Query(data),
1259       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1260        [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1261        [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1262     self.assertEqual(q.OldStyleQuery(data), [
1263       ["node1", "node44"],
1264       ["node25", "node1"],
1265       ["node20", "node1"],
1266       ])
1267     self.assertEqual(q.Query(reversed(data)),
1268       [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1269        [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1270        [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1271     self.assertEqual(q.OldStyleQuery(reversed(data)), [
1272       ["node20", "node1"],
1273       ["node25", "node1"],
1274       ["node1", "node44"],
1275       ])
1276
1277     # Name field, but no sorting, result must be in incoming order
1278     q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1279     self.assertFalse(q.RequestedData())
1280     self.assertEqual(q.Query(data, sort_by_name=False),
1281       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1282        [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1283        [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1284        [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1285     self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1286       ["node1", "node44"],
1287       ["node30", "node90"],
1288       ["node25", "node1"],
1289       ["node20", "node1"],
1290       ])
1291     self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1292       [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1293        [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1294        [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1295        [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1296     self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1297       ["node20", "node1"],
1298       ["node25", "node1"],
1299       ["node30", "node90"],
1300       ["node1", "node44"],
1301       ])
1302
1303   def testEqualNamesOrder(self):
1304     fielddefs = query._PrepareFieldList([
1305       (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1306        None, 0, lambda ctx, item: item["pnode"]),
1307       (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1308        None, 0, lambda ctx, item: item["num"]),
1309       ], [])
1310
1311     data = [
1312       { "pnode": "node1", "num": 100, },
1313       { "pnode": "node1", "num": 25, },
1314       { "pnode": "node2", "num": 90, },
1315       { "pnode": "node2", "num": 30, },
1316       ]
1317
1318     q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1319                     qfilter=["|", ["=", "pnode", "node1"],
1320                                   ["=", "pnode", "node2"],
1321                                   ["=", "pnode", "node1"]])
1322     self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1323                      msg="Did not return unique names")
1324     self.assertFalse(q.RequestedData())
1325     self.assertEqual(q.Query(data),
1326       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1327        [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1328        [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1329        [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1330     self.assertEqual(q.Query(data, sort_by_name=False),
1331       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1332        [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1333        [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1334        [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1335
1336     data = [
1337       { "pnode": "nodeX", "num": 50, },
1338       { "pnode": "nodeY", "num": 40, },
1339       { "pnode": "nodeX", "num": 30, },
1340       { "pnode": "nodeX", "num": 20, },
1341       { "pnode": "nodeM", "num": 10, },
1342       ]
1343
1344     q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1345                     qfilter=["|", ["=", "pnode", "nodeX"],
1346                                   ["=", "pnode", "nodeY"],
1347                                   ["=", "pnode", "nodeY"],
1348                                   ["=", "pnode", "nodeY"],
1349                                   ["=", "pnode", "nodeM"]])
1350     self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1351                      msg="Did not return unique names")
1352     self.assertFalse(q.RequestedData())
1353
1354     # First sorted by name, then input order
1355     self.assertEqual(q.Query(data, sort_by_name=True),
1356       [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1357        [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1358        [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1359        [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1360        [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1361
1362     # Input order
1363     self.assertEqual(q.Query(data, sort_by_name=False),
1364       [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1365        [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1366        [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1367        [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1368        [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1369
1370   def testFilter(self):
1371     (DK_A, DK_B) = range(1000, 1002)
1372
1373     fielddefs = query._PrepareFieldList([
1374       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1375        DK_A, 0, lambda ctx, item: item["name"]),
1376       (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1377        DK_B, 0, lambda ctx, item: item["other"]),
1378       ], [])
1379
1380     data = [
1381       { "name": "node1", "other": "foo", },
1382       { "name": "node2", "other": "bar", },
1383       { "name": "node3", "other": "Hello", },
1384       ]
1385
1386     # Empty filter
1387     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1388                     qfilter=["|"])
1389     self.assertTrue(q.RequestedNames() is None)
1390     self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1391     self.assertEqual(q.Query(data), [])
1392
1393     # Normal filter
1394     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1395                     qfilter=["=", "name", "node1"])
1396     self.assertEqual(q.RequestedNames(), ["node1"])
1397     self.assertEqual(q.Query(data),
1398       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1399
1400     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1401                     qfilter=(["|", ["=", "name", "node1"],
1402                                    ["=", "name", "node3"]]))
1403     self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1404     self.assertEqual(q.Query(data),
1405       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1406        [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1407
1408     # Complex filter
1409     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1410                     qfilter=(["|", ["=", "name", "node1"],
1411                                    ["|", ["=", "name", "node3"],
1412                                          ["=", "name", "node2"]],
1413                                    ["=", "name", "node3"]]))
1414     self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1415     self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1416     self.assertEqual(q.Query(data),
1417       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1418        [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1419        [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1420
1421     # Filter data type mismatch
1422     for i in [-1, 0, 1, 123, [], None, True, False]:
1423       self.assertRaises(errors.ParameterError, query.Query,
1424                         fielddefs, ["name", "other"], namefield="name",
1425                         qfilter=["=", "name", i])
1426
1427     # Negative filter
1428     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1429                     qfilter=["!", ["|", ["=", "name", "node1"],
1430                                         ["=", "name", "node3"]]])
1431     self.assertTrue(q.RequestedNames() is None)
1432     self.assertEqual(q.Query(data),
1433       [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1434
1435     # Not equal
1436     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1437                     qfilter=["!=", "name", "node3"])
1438     self.assertTrue(q.RequestedNames() is None)
1439     self.assertEqual(q.Query(data),
1440       [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1441        [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1442
1443     # Data type
1444     q = query.Query(fielddefs, [], namefield="name",
1445                     qfilter=["|", ["=", "other", "bar"],
1446                                   ["=", "name", "foo"]])
1447     self.assertTrue(q.RequestedNames() is None)
1448     self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1449     self.assertEqual(q.Query(data), [[]])
1450
1451     # Only one data type
1452     q = query.Query(fielddefs, ["other"], namefield="name",
1453                     qfilter=["=", "other", "bar"])
1454     self.assertTrue(q.RequestedNames() is None)
1455     self.assertEqual(q.RequestedData(), set([DK_B]))
1456     self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1457
1458     q = query.Query(fielddefs, [], namefield="name",
1459                     qfilter=["=", "other", "bar"])
1460     self.assertTrue(q.RequestedNames() is None)
1461     self.assertEqual(q.RequestedData(), set([DK_B]))
1462     self.assertEqual(q.Query(data), [[]])
1463
1464   def testFilterContains(self):
1465     fielddefs = query._PrepareFieldList([
1466       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1467        None, 0, lambda ctx, item: item["name"]),
1468       (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1469        None, 0, lambda ctx, item: item["other"]),
1470       ], [])
1471
1472     data = [
1473       { "name": "node2", "other": ["x", "y", "bar"], },
1474       { "name": "node3", "other": "Hello", },
1475       { "name": "node1", "other": ["a", "b", "foo"], },
1476       { "name": "empty", "other": []},
1477       ]
1478
1479     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1480                     qfilter=["=[]", "other", "bar"])
1481     self.assertTrue(q.RequestedNames() is None)
1482     self.assertEqual(q.Query(data), [
1483       [(constants.RS_NORMAL, "node2"),
1484        (constants.RS_NORMAL, ["x", "y", "bar"])],
1485       ])
1486
1487     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1488                     qfilter=["|", ["=[]", "other", "bar"],
1489                                   ["=[]", "other", "a"],
1490                                   ["=[]", "other", "b"]])
1491     self.assertTrue(q.RequestedNames() is None)
1492     self.assertEqual(q.Query(data), [
1493       [(constants.RS_NORMAL, "node1"),
1494        (constants.RS_NORMAL, ["a", "b", "foo"])],
1495       [(constants.RS_NORMAL, "node2"),
1496        (constants.RS_NORMAL, ["x", "y", "bar"])],
1497       ])
1498     self.assertEqual(q.OldStyleQuery(data), [
1499       ["node1", ["a", "b", "foo"]],
1500       ["node2", ["x", "y", "bar"]],
1501       ])
1502
1503     # Boolean test
1504     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1505                     qfilter=["?", "other"])
1506     self.assertEqual(q.OldStyleQuery(data), [
1507       ["node1", ["a", "b", "foo"]],
1508       ["node2", ["x", "y", "bar"]],
1509       ["node3", "Hello"],
1510       ])
1511
1512     q = query.Query(fielddefs, ["name", "other"], namefield="name",
1513                     qfilter=["!", ["?", "other"]])
1514     self.assertEqual(q.OldStyleQuery(data), [
1515       ["empty", []],
1516       ])
1517
1518   def testFilterHostname(self):
1519     fielddefs = query._PrepareFieldList([
1520       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1521        None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1522       ], [])
1523
1524     data = [
1525       { "name": "node1.example.com", },
1526       { "name": "node2.example.com", },
1527       { "name": "node2.example.net", },
1528       ]
1529
1530     q = query.Query(fielddefs, ["name"], namefield="name",
1531                     qfilter=["=", "name", "node2"])
1532     self.assertEqual(q.RequestedNames(), ["node2"])
1533     self.assertEqual(q.Query(data), [
1534       [(constants.RS_NORMAL, "node2.example.com")],
1535       [(constants.RS_NORMAL, "node2.example.net")],
1536       ])
1537
1538     q = query.Query(fielddefs, ["name"], namefield="name",
1539                     qfilter=["=", "name", "node1"])
1540     self.assertEqual(q.RequestedNames(), ["node1"])
1541     self.assertEqual(q.Query(data), [
1542       [(constants.RS_NORMAL, "node1.example.com")],
1543       ])
1544
1545     q = query.Query(fielddefs, ["name"], namefield="name",
1546                     qfilter=["=", "name", "othername"])
1547     self.assertEqual(q.RequestedNames(), ["othername"])
1548     self.assertEqual(q.Query(data), [])
1549
1550     q = query.Query(fielddefs, ["name"], namefield="name",
1551                     qfilter=["|", ["=", "name", "node1.example.com"],
1552                                   ["=", "name", "node2"]])
1553     self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1554     self.assertEqual(q.Query(data), [
1555       [(constants.RS_NORMAL, "node1.example.com")],
1556       [(constants.RS_NORMAL, "node2.example.com")],
1557       [(constants.RS_NORMAL, "node2.example.net")],
1558       ])
1559     self.assertEqual(q.OldStyleQuery(data), [
1560       ["node1.example.com"],
1561       ["node2.example.com"],
1562       ["node2.example.net"],
1563       ])
1564
1565     q = query.Query(fielddefs, ["name"], namefield="name",
1566                     qfilter=["!=", "name", "node1"])
1567     self.assertTrue(q.RequestedNames() is None)
1568     self.assertEqual(q.Query(data), [
1569       [(constants.RS_NORMAL, "node2.example.com")],
1570       [(constants.RS_NORMAL, "node2.example.net")],
1571       ])
1572     self.assertEqual(q.OldStyleQuery(data), [
1573       ["node2.example.com"],
1574       ["node2.example.net"],
1575       ])
1576
1577   def testFilterBoolean(self):
1578     fielddefs = query._PrepareFieldList([
1579       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1580        None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1581       (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1582        None, 0, lambda ctx, item: item["value"]),
1583       ], [])
1584
1585     data = [
1586       { "name": "node1", "value": False, },
1587       { "name": "node2", "value": True, },
1588       { "name": "node3", "value": True, },
1589       ]
1590
1591     q = query.Query(fielddefs, ["name", "value"],
1592                     qfilter=["|", ["=", "value", False],
1593                                   ["=", "value", True]])
1594     self.assertTrue(q.RequestedNames() is None)
1595     self.assertEqual(q.Query(data), [
1596       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1597       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1598       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1599       ])
1600
1601     q = query.Query(fielddefs, ["name", "value"],
1602                     qfilter=["|", ["=", "value", False],
1603                                   ["!", ["=", "value", False]]])
1604     self.assertTrue(q.RequestedNames() is None)
1605     self.assertEqual(q.Query(data), [
1606       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1607       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1608       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1609       ])
1610
1611     # Comparing bool with string
1612     for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1613       self.assertRaises(errors.ParameterError, query.Query,
1614                         fielddefs, ["name", "value"],
1615                         qfilter=["=", "value", i])
1616
1617     # Truth filter
1618     q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1619     self.assertTrue(q.RequestedNames() is None)
1620     self.assertEqual(q.Query(data), [
1621       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1622       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1623       ])
1624
1625     # Negative bool filter
1626     q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1627     self.assertTrue(q.RequestedNames() is None)
1628     self.assertEqual(q.Query(data), [
1629       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1630       ])
1631
1632     # Complex truth filter
1633     q = query.Query(fielddefs, ["name", "value"],
1634                     qfilter=["|", ["&", ["=", "name", "node1"],
1635                                         ["!", ["?", "value"]]],
1636                                   ["?", "value"]])
1637     self.assertTrue(q.RequestedNames() is None)
1638     self.assertEqual(q.Query(data), [
1639       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1640       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1641       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1642       ])
1643
1644   def testFilterRegex(self):
1645     fielddefs = query._PrepareFieldList([
1646       (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1647        None, 0, lambda ctx, item: item["name"]),
1648       ], [])
1649
1650     data = [
1651       { "name": "node1.example.com", },
1652       { "name": "node2.site.example.com", },
1653       { "name": "node2.example.net", },
1654
1655       # Empty name
1656       { "name": "", },
1657       ]
1658
1659     q = query.Query(fielddefs, ["name"], namefield="name",
1660                     qfilter=["=~", "name", "site"])
1661     self.assertTrue(q.RequestedNames() is None)
1662     self.assertEqual(q.Query(data), [
1663       [(constants.RS_NORMAL, "node2.site.example.com")],
1664       ])
1665
1666     q = query.Query(fielddefs, ["name"], namefield="name",
1667                     qfilter=["=~", "name", "^node2"])
1668     self.assertTrue(q.RequestedNames() is None)
1669     self.assertEqual(q.Query(data), [
1670       [(constants.RS_NORMAL, "node2.example.net")],
1671       [(constants.RS_NORMAL, "node2.site.example.com")],
1672       ])
1673
1674     q = query.Query(fielddefs, ["name"], namefield="name",
1675                     qfilter=["=~", "name", r"(?i)\.COM$"])
1676     self.assertTrue(q.RequestedNames() is None)
1677     self.assertEqual(q.Query(data), [
1678       [(constants.RS_NORMAL, "node1.example.com")],
1679       [(constants.RS_NORMAL, "node2.site.example.com")],
1680       ])
1681
1682     q = query.Query(fielddefs, ["name"], namefield="name",
1683                     qfilter=["=~", "name", r"."])
1684     self.assertTrue(q.RequestedNames() is None)
1685     self.assertEqual(q.Query(data), [
1686       [(constants.RS_NORMAL, "node1.example.com")],
1687       [(constants.RS_NORMAL, "node2.example.net")],
1688       [(constants.RS_NORMAL, "node2.site.example.com")],
1689       ])
1690
1691     q = query.Query(fielddefs, ["name"], namefield="name",
1692                     qfilter=["=~", "name", r"^$"])
1693     self.assertTrue(q.RequestedNames() is None)
1694     self.assertEqual(q.Query(data), [
1695       [(constants.RS_NORMAL, "")],
1696       ])
1697
1698     # Invalid regular expression
1699     self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1700                       qfilter=["=~", "name", r"["])
1701
1702
1703 if __name__ == "__main__":
1704   testutils.GanetiTestProgram()