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