Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 861610e9

History | View | Annotate | Download (64.4 kB)

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

    
4
# Copyright (C) 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for testing ganeti.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
    nodes = [
326
      objects.Node(name="node1", drained=False),
327
      objects.Node(name="node2", drained=True),
328
      objects.Node(name="node3", drained=False),
329
      ]
330
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
331
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None,
332
                                None)
333

    
334
      q = self._Create(["name", "drained"])
335
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
336
      self.assertEqual(q.Query(nqd),
337
                       [[(constants.RS_NORMAL, "node1"),
338
                         (constants.RS_NORMAL, False)],
339
                        [(constants.RS_NORMAL, "node2"),
340
                         (constants.RS_NORMAL, True)],
341
                        [(constants.RS_NORMAL, "node3"),
342
                         (constants.RS_NORMAL, False)],
343
                       ])
344
      self.assertEqual(q.OldStyleQuery(nqd),
345
                       [["node1", False],
346
                        ["node2", True],
347
                        ["node3", False]])
348

    
349
  def test(self):
350
    selected = query.NODE_FIELDS.keys()
351
    field_index = dict((field, idx) for idx, field in enumerate(selected))
352

    
353
    q = self._Create(selected)
354
    self.assertEqual(q.RequestedData(),
355
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
356
                          query.NQ_GROUP, query.NQ_OOB]))
357

    
358
    cluster = objects.Cluster(cluster_name="testcluster",
359
      hvparams=constants.HVC_DEFAULTS,
360
      beparams={
361
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
362
        },
363
      nicparams={
364
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
365
        },
366
      ndparams=constants.NDC_DEFAULTS,
367
        )
368

    
369
    node_names = ["node%s" % i for i in range(20)]
370
    master_name = node_names[3]
371
    nodes = [
372
      objects.Node(name=name,
373
                   primary_ip="192.0.2.%s" % idx,
374
                   secondary_ip="192.0.100.%s" % idx,
375
                   serial_no=7789 * idx,
376
                   master_candidate=(name != master_name and idx % 3 == 0),
377
                   offline=False,
378
                   drained=False,
379
                   powered=True,
380
                   vm_capable=True,
381
                   master_capable=False,
382
                   ndparams={},
383
                   group="default",
384
                   ctime=1290006900,
385
                   mtime=1290006913,
386
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
387
      for idx, name in enumerate(node_names)
388
      ]
389

    
390
    master_node = nodes[3]
391
    master_node.AddTag("masternode")
392
    master_node.AddTag("another")
393
    master_node.AddTag("tag")
394
    master_node.ctime = None
395
    master_node.mtime = None
396
    assert master_node.name == master_name
397

    
398
    live_data_name = node_names[4]
399
    assert live_data_name != master_name
400

    
401
    fake_live_data = {
402
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
403
      "cnodes": 4,
404
      "csockets": 4,
405
      "ctotal": 8,
406
      "mnode": 128,
407
      "mfree": 100,
408
      "mtotal": 4096,
409
      "dfree": 5 * 1024 * 1024,
410
      "dtotal": 100 * 1024 * 1024,
411
      }
412

    
413
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
414
            sorted(fake_live_data.keys()))
415

    
416
    live_data = dict.fromkeys(node_names, {})
417
    live_data[live_data_name] = \
418
      dict((query._NODE_LIVE_FIELDS[name][2], value)
419
           for name, value in fake_live_data.items())
420

    
421
    node_to_primary = dict((name, set()) for name in node_names)
422
    node_to_primary[master_name].update(["inst1", "inst2"])
423

    
424
    node_to_secondary = dict((name, set()) for name in node_names)
425
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
426

    
427
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
428
    groups = {
429
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
430
      }
431

    
432
    oob_not_powered_node = node_names[0]
433
    nodes[0].powered = False
434
    oob_support = dict((name, False) for name in node_names)
435
    oob_support[master_name] = True
436
    oob_support[oob_not_powered_node] = True
437

    
438
    master_node.group = ng_uuid
439

    
440
    nqd = query.NodeQueryData(nodes, live_data, master_name,
441
                              node_to_primary, node_to_secondary, groups,
442
                              oob_support, cluster)
443
    result = q.Query(nqd)
444
    self.assert_(compat.all(len(row) == len(selected) for row in result))
445
    self.assertEqual([row[field_index["name"]] for row in result],
446
                     [(constants.RS_NORMAL, name) for name in node_names])
447

    
448
    node_to_row = dict((row[field_index["name"]][1], idx)
449
                       for idx, row in enumerate(result))
450

    
451
    master_row = result[node_to_row[master_name]]
452
    self.assert_(master_row[field_index["master"]])
453
    self.assert_(master_row[field_index["role"]], "M")
454
    self.assertEqual(master_row[field_index["group"]],
455
                     (constants.RS_NORMAL, "ng1"))
456
    self.assertEqual(master_row[field_index["group.uuid"]],
457
                     (constants.RS_NORMAL, ng_uuid))
458
    self.assertEqual(master_row[field_index["ctime"]],
459
                     (constants.RS_UNAVAIL, None))
460
    self.assertEqual(master_row[field_index["mtime"]],
461
                     (constants.RS_UNAVAIL, None))
462

    
463
    self.assert_(row[field_index["pip"]] == node.primary_ip and
464
                 row[field_index["sip"]] == node.secondary_ip and
465
                 set(row[field_index["tags"]]) == node.GetTags() and
466
                 row[field_index["serial_no"]] == node.serial_no and
467
                 row[field_index["role"]] == query._GetNodeRole(node,
468
                                                                master_name) and
469
                 (node.name == master_name or
470
                  (row[field_index["group"]] == "<unknown>" and
471
                   row[field_index["group.uuid"]] is None and
472
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
473
                                                 node.ctime) and
474
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
475
                                                 node.mtime) and
476
                   row[field_index["powered"]] == (constants.RS_NORMAL,
477
                                                   True))) or
478
                 (node.name == oob_not_powered_node and
479
                  row[field_index["powered"]] == (constants.RS_NORMAL,
480
                                                  False)) or
481
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
482
                 for row, node in zip(result, nodes))
483

    
484
    live_data_row = result[node_to_row[live_data_name]]
485

    
486
    for (field, value) in fake_live_data.items():
487
      self.assertEqual(live_data_row[field_index[field]],
488
                       (constants.RS_NORMAL, value))
489

    
490
    self.assertEqual(master_row[field_index["pinst_cnt"]],
491
                     (constants.RS_NORMAL, 2))
492
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
493
                     (constants.RS_NORMAL, 3))
494
    self.assertEqual(master_row[field_index["pinst_list"]],
495
                     (constants.RS_NORMAL,
496
                      list(node_to_primary[master_name])))
497
    self.assertEqual(live_data_row[field_index["sinst_list"]],
498
                     (constants.RS_NORMAL,
499
                      list(node_to_secondary[live_data_name])))
500

    
501
  def testGetLiveNodeField(self):
502
    nodes = [
503
      objects.Node(name="node1", drained=False, offline=False,
504
                   vm_capable=True),
505
      objects.Node(name="node2", drained=True, offline=False,
506
                   vm_capable=True),
507
      objects.Node(name="node3", drained=False, offline=False,
508
                   vm_capable=True),
509
      objects.Node(name="node4", drained=False, offline=True,
510
                   vm_capable=True),
511
      objects.Node(name="node5", drained=False, offline=False,
512
                   vm_capable=False),
513
      ]
514
    live_data = dict.fromkeys([node.name for node in nodes], {})
515

    
516
    # No data
517
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
518
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
519
                                             nqd, nodes[0]),
520
                     query._FS_NODATA)
521

    
522
    # Missing field
523
    ctx = _QueryData(None, curlive_data={
524
      "some": 1,
525
      "other": 2,
526
      })
527
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
528
                                             ctx, nodes[0]),
529
                     query._FS_UNAVAIL)
530

    
531
    # Wrong format/datatype
532
    ctx = _QueryData(None, curlive_data={
533
      "hello": ["Hello World"],
534
      "other": 2,
535
      })
536
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
537
                                             ctx, nodes[0]),
538
                     query._FS_UNAVAIL)
539

    
540
    # Offline node
541
    assert nodes[3].offline
542
    ctx = _QueryData(None, curlive_data={})
543
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
544
                                             ctx, nodes[3]),
545
                     query._FS_OFFLINE, None)
546

    
547
    # Wrong field type
548
    ctx = _QueryData(None, curlive_data={"hello": 123})
549
    self.assertRaises(AssertionError, query._GetLiveNodeField,
550
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
551

    
552
    # Non-vm_capable node
553
    assert not nodes[4].vm_capable
554
    ctx = _QueryData(None, curlive_data={})
555
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
556
                                             ctx, nodes[4]),
557
                     query._FS_UNAVAIL, None)
558

    
559

    
560
class TestInstanceQuery(unittest.TestCase):
561
  def _Create(self, selected):
562
    return query.Query(query.INSTANCE_FIELDS, selected)
563

    
564
  def testSimple(self):
565
    q = self._Create(["name", "be/maxmem", "ip"])
566
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
567

    
568
    cluster = objects.Cluster(cluster_name="testcluster",
569
      hvparams=constants.HVC_DEFAULTS,
570
      beparams={
571
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
572
        },
573
      nicparams={
574
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
575
        },
576
      os_hvp={},
577
      osparams={})
578

    
579
    instances = [
580
      objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
581
                       nics=[], os="deb1"),
582
      objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
583
        os="foomoo",
584
        beparams={
585
          constants.BE_MAXMEM: 512,
586
        }),
587
      objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
588
        os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
589
      ]
590

    
591
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
592
                                  set(), {}, None, None)
593
    self.assertEqual(q.Query(iqd),
594
      [[(constants.RS_NORMAL, "inst1"),
595
        (constants.RS_NORMAL, 128),
596
        (constants.RS_UNAVAIL, None),
597
       ],
598
       [(constants.RS_NORMAL, "inst2"),
599
        (constants.RS_NORMAL, 512),
600
        (constants.RS_UNAVAIL, None),
601
       ],
602
       [(constants.RS_NORMAL, "inst3"),
603
        (constants.RS_NORMAL, 128),
604
        (constants.RS_NORMAL, "192.0.2.99"),
605
       ]])
606
    self.assertEqual(q.OldStyleQuery(iqd),
607
      [["inst1", 128, None],
608
       ["inst2", 512, None],
609
       ["inst3", 128, "192.0.2.99"]])
610

    
611
  def test(self):
612
    selected = query.INSTANCE_FIELDS.keys()
613
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
614

    
615
    macs = ["00:11:22:%02x:%02x:%02x" % (i % 255, i % 3, (i * 123) % 255)
616
            for i in range(20)]
617

    
618
    q = self._Create(selected)
619
    self.assertEqual(q.RequestedData(),
620
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
621
                          query.IQ_CONSOLE, query.IQ_NODES]))
622

    
623
    cluster = objects.Cluster(cluster_name="testcluster",
624
      hvparams=constants.HVC_DEFAULTS,
625
      beparams={
626
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
627
        },
628
      nicparams={
629
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
630
        },
631
      os_hvp={},
632
      tcpudp_port_pool=set(),
633
      osparams={
634
        "deb99": {
635
          "clean_install": "yes",
636
          },
637
        })
638

    
639
    offline_nodes = ["nodeoff1", "nodeoff2"]
640
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
641
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
642

    
643
    instances = [
644
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
645
        uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
646
        ctime=1291244000, mtime=1291244400, serial_no=30,
647
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_PVM,
648
        os="linux1",
649
        primary_node="node1",
650
        disk_template=constants.DT_PLAIN,
651
        disks=[],
652
        osparams={}),
653
      objects.Instance(name="inst2", hvparams={}, nics=[],
654
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
655
        ctime=1291211000, mtime=1291211077, serial_no=1,
656
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
657
        os="deb99",
658
        primary_node="node5",
659
        disk_template=constants.DT_DISKLESS,
660
        disks=[],
661
        beparams={
662
          constants.BE_MAXMEM: 512,
663
          constants.BE_MINMEM: 256,
664
        },
665
        osparams={}),
666
      objects.Instance(name="inst3", hvparams={}, beparams={},
667
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
668
        ctime=1291011000, mtime=1291013000, serial_no=1923,
669
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_KVM,
670
        os="busybox",
671
        primary_node="node6",
672
        disk_template=constants.DT_DRBD8,
673
        disks=[],
674
        nics=[
675
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
676
                      nicparams={
677
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
678
                        }),
679
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
680
          ],
681
        osparams={}),
682
      objects.Instance(name="inst4", hvparams={}, beparams={},
683
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
684
        ctime=1291244390, mtime=1291244395, serial_no=25,
685
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_PVM,
686
        os="linux1",
687
        primary_node="nodeoff2",
688
        disk_template=constants.DT_DRBD8,
689
        disks=[],
690
        nics=[
691
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
692
                      nicparams={
693
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
694
                        }),
695
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
696
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
697
                      nicparams={
698
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
699
                        }),
700
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
701
                      nicparams={
702
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
703
                        constants.NIC_LINK: "eth123",
704
                        }),
705
          ],
706
        osparams={}),
707
      objects.Instance(name="inst5", hvparams={}, nics=[],
708
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
709
        ctime=1231211000, mtime=1261200000, serial_no=3,
710
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
711
        os="deb99",
712
        primary_node="nodebad2",
713
        disk_template=constants.DT_DISKLESS,
714
        disks=[],
715
        beparams={
716
          constants.BE_MAXMEM: 512,
717
          constants.BE_MINMEM: 512,
718
        },
719
        osparams={}),
720
      objects.Instance(name="inst6", hvparams={}, nics=[],
721
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
722
        ctime=7513, mtime=11501, serial_no=13390,
723
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
724
        os="deb99",
725
        primary_node="node7",
726
        disk_template=constants.DT_DISKLESS,
727
        disks=[],
728
        beparams={
729
          constants.BE_MAXMEM: 768,
730
          constants.BE_MINMEM: 256,
731
        },
732
        osparams={
733
          "clean_install": "no",
734
          }),
735
      objects.Instance(name="inst7", hvparams={}, nics=[],
736
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
737
        ctime=None, mtime=None, serial_no=1947,
738
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
739
        os="deb99",
740
        primary_node="node6",
741
        disk_template=constants.DT_DISKLESS,
742
        disks=[],
743
        beparams={},
744
        osparams={}),
745
      objects.Instance(name="inst8", hvparams={}, nics=[],
746
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920f",
747
        ctime=None, mtime=None, serial_no=19478,
748
        admin_state=constants.ADMINST_OFFLINE, hypervisor=constants.HT_XEN_HVM,
749
        os="deb99",
750
        primary_node="node6",
751
        disk_template=constants.DT_DISKLESS,
752
        disks=[],
753
        beparams={},
754
        osparams={}),
755
      ]
756

    
757
    assert not utils.FindDuplicates(inst.name for inst in instances)
758

    
759
    instbyname = dict((inst.name, inst) for inst in instances)
760

    
761
    disk_usage = dict((inst.name,
762
                       cmdlib._ComputeDiskSize(inst.disk_template,
763
                                               [{"size": disk.size}
764
                                                for disk in inst.disks]))
765
                      for inst in instances)
766

    
767
    inst_bridges = {
768
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
769
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
770
                None, "eth123"],
771
      }
772

    
773
    live_data = {
774
      "inst2": {
775
        "vcpus": 3,
776
        },
777
      "inst4": {
778
        "memory": 123,
779
        },
780
      "inst6": {
781
        "memory": 768,
782
        },
783
      "inst7": {
784
        "vcpus": 3,
785
        },
786
      }
787
    wrongnode_inst = set(["inst7"])
788

    
789
    consinfo = dict((inst.name, None) for inst in instances)
790
    consinfo["inst7"] = \
791
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
792
                              host=instbyname["inst7"].primary_node,
793
                              user=constants.GANETI_RUNAS,
794
                              command=["hostname"]).ToDict()
795

    
796
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
797
                                  offline_nodes, bad_nodes, live_data,
798
                                  wrongnode_inst, consinfo, {}, {})
799
    result = q.Query(iqd)
800
    self.assertEqual(len(result), len(instances))
801
    self.assert_(compat.all(len(row) == len(selected)
802
                            for row in result))
803

    
804
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
805
           "Offline nodes not included in bad nodes"
806

    
807
    tested_status = set()
808

    
809
    for (inst, row) in zip(instances, result):
810
      assert inst.primary_node in nodes
811

    
812
      self.assertEqual(row[fieldidx["name"]],
813
                       (constants.RS_NORMAL, inst.name))
814

    
815
      if inst.primary_node in offline_nodes:
816
        exp_status = constants.INSTST_NODEOFFLINE
817
      elif inst.primary_node in bad_nodes:
818
        exp_status = constants.INSTST_NODEDOWN
819
      elif inst.name in live_data:
820
        if inst.name in wrongnode_inst:
821
          exp_status = constants.INSTST_WRONGNODE
822
        elif inst.admin_state == constants.ADMINST_UP:
823
          exp_status = constants.INSTST_RUNNING
824
        else:
825
          exp_status = constants.INSTST_ERRORUP
826
      elif inst.admin_state == constants.ADMINST_UP:
827
        exp_status = constants.INSTST_ERRORDOWN
828
      elif inst.admin_state == constants.ADMINST_DOWN:
829
        exp_status = constants.INSTST_ADMINDOWN
830
      else:
831
        exp_status = constants.INSTST_ADMINOFFLINE
832

    
833
      self.assertEqual(row[fieldidx["status"]],
834
                       (constants.RS_NORMAL, exp_status))
835

    
836
      (_, status) = row[fieldidx["status"]]
837
      tested_status.add(status)
838

    
839
      #FIXME(dynmem): check oper_ram vs min/max mem
840
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
841
        if inst.primary_node in bad_nodes:
842
          exp = (constants.RS_NODATA, None)
843
        elif inst.name in live_data:
844
          value = live_data[inst.name].get(livefield, None)
845
          if value is None:
846
            exp = (constants.RS_UNAVAIL, None)
847
          else:
848
            exp = (constants.RS_NORMAL, value)
849
        else:
850
          exp = (constants.RS_UNAVAIL, None)
851

    
852
        self.assertEqual(row[fieldidx[field]], exp)
853

    
854
      bridges = inst_bridges.get(inst.name, [])
855
      self.assertEqual(row[fieldidx["nic.bridges"]],
856
                       (constants.RS_NORMAL, bridges))
857
      if bridges:
858
        self.assertEqual(row[fieldidx["bridge"]],
859
                         (constants.RS_NORMAL, bridges[0]))
860
      else:
861
        self.assertEqual(row[fieldidx["bridge"]],
862
                         (constants.RS_UNAVAIL, None))
863

    
864
      for i in range(constants.MAX_NICS):
865
        if i < len(bridges) and bridges[i] is not None:
866
          exp = (constants.RS_NORMAL, bridges[i])
867
        else:
868
          exp = (constants.RS_UNAVAIL, None)
869
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
870

    
871
      if inst.primary_node in bad_nodes:
872
        exp = (constants.RS_NODATA, None)
873
      else:
874
        exp = (constants.RS_NORMAL, inst.name in live_data)
875
      self.assertEqual(row[fieldidx["oper_state"]], exp)
876

    
877
      cust_exp = (constants.RS_NORMAL, {})
878
      if inst.os == "deb99":
879
        if inst.name == "inst6":
880
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
881
          cust_exp = exp
882
        else:
883
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
884
      else:
885
        exp = (constants.RS_NORMAL, {})
886
      self.assertEqual(row[fieldidx["osparams"]], exp)
887
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
888

    
889
      usage = disk_usage[inst.name]
890
      if usage is None:
891
        usage = 0
892
      self.assertEqual(row[fieldidx["disk_usage"]],
893
                       (constants.RS_NORMAL, usage))
894

    
895
      for alias, target in [("sda_size", "disk.size/0"),
896
                            ("sdb_size", "disk.size/1"),
897
                            ("vcpus", "be/vcpus"),
898
                            ("ip", "nic.ip/0"),
899
                            ("mac", "nic.mac/0"),
900
                            ("bridge", "nic.bridge/0"),
901
                            ("nic_mode", "nic.mode/0"),
902
                            ("nic_link", "nic.link/0"),
903
                            ]:
904
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
905

    
906
      for field in ["ctime", "mtime"]:
907
        if getattr(inst, field) is None:
908
          # No ctime/mtime
909
          exp = (constants.RS_UNAVAIL, None)
910
        else:
911
          exp = (constants.RS_NORMAL, getattr(inst, field))
912
        self.assertEqual(row[fieldidx[field]], exp)
913

    
914
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
915

    
916
    # Ensure all possible status' have been tested
917
    self.assertEqual(tested_status, constants.INSTST_ALL)
918

    
919
  def _CheckInstanceConsole(self, instance, (status, consdata)):
920
    if instance.name == "inst7":
921
      self.assertEqual(status, constants.RS_NORMAL)
922
      console = objects.InstanceConsole.FromDict(consdata)
923
      self.assertTrue(console.Validate())
924
      self.assertEqual(console.host, instance.primary_node)
925
    else:
926
      self.assertEqual(status, constants.RS_UNAVAIL)
927

    
928

    
929
class TestGroupQuery(unittest.TestCase):
930

    
931
  def setUp(self):
932
    self.groups = [
933
      objects.NodeGroup(name="default",
934
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
935
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
936
      objects.NodeGroup(name="restricted",
937
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
938
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
939
      ]
940

    
941
  def _Create(self, selected):
942
    return query.Query(query.GROUP_FIELDS, selected)
943

    
944
  def testSimple(self):
945
    q = self._Create(["name", "uuid", "alloc_policy"])
946
    gqd = query.GroupQueryData(self.groups, None, None)
947

    
948
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
949

    
950
    self.assertEqual(q.Query(gqd),
951
      [[(constants.RS_NORMAL, "default"),
952
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
953
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
954
        ],
955
       [(constants.RS_NORMAL, "restricted"),
956
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
957
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
958
        ],
959
       ])
960

    
961
  def testNodes(self):
962
    groups_to_nodes = {
963
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
964
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
965
      }
966

    
967
    q = self._Create(["name", "node_cnt", "node_list"])
968
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
969

    
970
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
971

    
972
    self.assertEqual(q.Query(gqd),
973
                     [[(constants.RS_NORMAL, "default"),
974
                       (constants.RS_NORMAL, 2),
975
                       (constants.RS_NORMAL, ["node1", "node2"]),
976
                       ],
977
                      [(constants.RS_NORMAL, "restricted"),
978
                       (constants.RS_NORMAL, 3),
979
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
980
                       ],
981
                      ])
982

    
983
  def testInstances(self):
984
    groups_to_instances = {
985
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
986
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
987
      }
988

    
989
    q = self._Create(["pinst_cnt", "pinst_list"])
990
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
991

    
992
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
993

    
994
    self.assertEqual(q.Query(gqd),
995
                     [[(constants.RS_NORMAL, 2),
996
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
997
                       ],
998
                      [(constants.RS_NORMAL, 3),
999
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1000
                       ],
1001
                      ])
1002

    
1003

    
1004
class TestOsQuery(unittest.TestCase):
1005
  def _Create(self, selected):
1006
    return query.Query(query.OS_FIELDS, selected)
1007

    
1008
  def test(self):
1009
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1010
    api_versions = [10, 0, 15, 5]
1011
    parameters = ["zpar3", "apar9"]
1012

    
1013
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1014
    assert (api_versions != sorted(api_versions) and
1015
            api_versions != utils.NiceSort(variants))
1016
    assert (parameters != sorted(parameters) and
1017
            parameters != utils.NiceSort(parameters))
1018

    
1019
    data = [
1020
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1021
                   variants=set(), api_versions=set(), parameters=set(),
1022
                   node_status={ "some": "status", }),
1023
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1024
                   variants=set(variants),
1025
                   api_versions=set(api_versions),
1026
                   parameters=set(parameters),
1027
                   node_status={ "some": "other", "status": None, }),
1028
      ]
1029

    
1030

    
1031
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1032
                      "api_versions", "parameters", "node_status"])
1033
    self.assertEqual(q.RequestedData(), set([]))
1034
    self.assertEqual(q.Query(data),
1035
                     [[(constants.RS_NORMAL, "debian"),
1036
                       (constants.RS_NORMAL, False),
1037
                       (constants.RS_NORMAL, False),
1038
                       (constants.RS_NORMAL, False),
1039
                       (constants.RS_NORMAL, []),
1040
                       (constants.RS_NORMAL, []),
1041
                       (constants.RS_NORMAL, []),
1042
                       (constants.RS_NORMAL, {"some": "status"})],
1043
                      [(constants.RS_NORMAL, "dos"),
1044
                       (constants.RS_NORMAL, True),
1045
                       (constants.RS_NORMAL, False),
1046
                       (constants.RS_NORMAL, True),
1047
                       (constants.RS_NORMAL,
1048
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1049
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1050
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1051
                       (constants.RS_NORMAL,
1052
                        { "some": "other", "status": None, })
1053
                       ]])
1054

    
1055

    
1056
class TestQueryFields(unittest.TestCase):
1057
  def testAllFields(self):
1058
    for fielddefs in query.ALL_FIELD_LISTS:
1059
      result = query.QueryFields(fielddefs, None)
1060
      self.assert_(isinstance(result, dict))
1061
      response = objects.QueryFieldsResponse.FromDict(result)
1062
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1063
        [(fdef2.name, fdef2.title)
1064
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1065
                                                key=lambda x: x[0].name)])
1066

    
1067
  def testSomeFields(self):
1068
    rnd = random.Random(5357)
1069

    
1070
    for _ in range(10):
1071
      for fielddefs in query.ALL_FIELD_LISTS:
1072
        if len(fielddefs) > 20:
1073
          sample_size = rnd.randint(5, 20)
1074
        else:
1075
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1076
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1077
                                                         sample_size)]
1078
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1079
        self.assert_(isinstance(result, dict))
1080
        response = objects.QueryFieldsResponse.FromDict(result)
1081
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1082
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1083

    
1084

    
1085
class TestQueryFilter(unittest.TestCase):
1086
  def testRequestedNames(self):
1087
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1088

    
1089
    for fielddefs in query.ALL_FIELD_LISTS:
1090
      assert "name" in fielddefs
1091

    
1092
      # No name field
1093
      q = query.Query(fielddefs, ["name"], qfilter=["=", "name", "abc"],
1094
                      namefield=None)
1095
      self.assertEqual(q.RequestedNames(), None)
1096

    
1097
      # No filter
1098
      q = query.Query(fielddefs, ["name"], qfilter=None, namefield="name")
1099
      self.assertEqual(q.RequestedNames(), None)
1100

    
1101
      # Check empty query
1102
      q = query.Query(fielddefs, ["name"], qfilter=["|"], namefield="name")
1103
      self.assertEqual(q.RequestedNames(), None)
1104

    
1105
      # Check order
1106
      q = query.Query(fielddefs, ["name"], qfilter=["|"] + innerfilter,
1107
                      namefield="name")
1108
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1109

    
1110
      # Check reverse order
1111
      q = query.Query(fielddefs, ["name"],
1112
                      qfilter=["|"] + list(reversed(innerfilter)),
1113
                      namefield="name")
1114
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1115

    
1116
      # Duplicates
1117
      q = query.Query(fielddefs, ["name"],
1118
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1119
                      namefield="name")
1120
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1121

    
1122
      # Unknown name field
1123
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1124
                        namefield="_unknown_field_")
1125

    
1126
      # Filter with AND
1127
      q = query.Query(fielddefs, ["name"],
1128
                      qfilter=["|", ["=", "name", "foo"],
1129
                                    ["&", ["=", "name", ""]]],
1130
                      namefield="name")
1131
      self.assertTrue(q.RequestedNames() is None)
1132

    
1133
      # Filter with NOT
1134
      q = query.Query(fielddefs, ["name"],
1135
                      qfilter=["|", ["=", "name", "foo"],
1136
                                    ["!", ["=", "name", ""]]],
1137
                      namefield="name")
1138
      self.assertTrue(q.RequestedNames() is None)
1139

    
1140
      # Filter with only OR (names must be in correct order)
1141
      q = query.Query(fielddefs, ["name"],
1142
                      qfilter=["|", ["=", "name", "x17361"],
1143
                                    ["|", ["=", "name", "x22015"]],
1144
                                    ["|", ["|", ["=", "name", "x13193"]]],
1145
                                    ["=", "name", "x15215"]],
1146
                      namefield="name")
1147
      self.assertEqual(q.RequestedNames(),
1148
                       ["x17361", "x22015", "x13193", "x15215"])
1149

    
1150
  @staticmethod
1151
  def _GenNestedFilter(op, depth):
1152
    nested = ["=", "name", "value"]
1153
    for i in range(depth):
1154
      nested = [op, nested]
1155
    return nested
1156

    
1157
  def testCompileFilter(self):
1158
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1159

    
1160
    checks = [
1161
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1162
      ["=", "_unknown_field", "value"],
1163
      self._GenNestedFilter("|", levels_max),
1164
      self._GenNestedFilter("|", levels_max * 3),
1165
      self._GenNestedFilter("!", levels_max),
1166
      ]
1167

    
1168
    for fielddefs in query.ALL_FIELD_LISTS:
1169
      for qfilter in checks:
1170
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1171
                          fielddefs, None, qfilter)
1172

    
1173
      for op in ["|", "!"]:
1174
        qfilter = self._GenNestedFilter(op, levels_max - 1)
1175
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1176
                                                      qfilter)))
1177

    
1178
  def testQueryInputOrder(self):
1179
    fielddefs = query._PrepareFieldList([
1180
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1181
       None, 0, lambda ctx, item: item["pnode"]),
1182
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1183
       None, 0, lambda ctx, item: item["snode"]),
1184
      ], [])
1185

    
1186
    data = [
1187
      { "pnode": "node1", "snode": "node44", },
1188
      { "pnode": "node30", "snode": "node90", },
1189
      { "pnode": "node25", "snode": "node1", },
1190
      { "pnode": "node20", "snode": "node1", },
1191
      ]
1192

    
1193
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1194

    
1195
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1196
                    qfilter=qfilter)
1197
    self.assertTrue(q.RequestedNames() is None)
1198
    self.assertFalse(q.RequestedData())
1199
    self.assertEqual(q.Query(data),
1200
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1201
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1202
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1203

    
1204
    # Try again with reversed input data
1205
    self.assertEqual(q.Query(reversed(data)),
1206
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1207
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1208
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1209

    
1210
    # No name field, result must be in incoming order
1211
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1212
                    qfilter=qfilter)
1213
    self.assertFalse(q.RequestedData())
1214
    self.assertEqual(q.Query(data),
1215
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1216
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1217
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1218
    self.assertEqual(q.OldStyleQuery(data), [
1219
      ["node1", "node44"],
1220
      ["node25", "node1"],
1221
      ["node20", "node1"],
1222
      ])
1223
    self.assertEqual(q.Query(reversed(data)),
1224
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1225
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1226
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1227
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1228
      ["node20", "node1"],
1229
      ["node25", "node1"],
1230
      ["node1", "node44"],
1231
      ])
1232

    
1233
    # Name field, but no sorting, result must be in incoming order
1234
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1235
    self.assertFalse(q.RequestedData())
1236
    self.assertEqual(q.Query(data, sort_by_name=False),
1237
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1238
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1239
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1240
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1241
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1242
      ["node1", "node44"],
1243
      ["node30", "node90"],
1244
      ["node25", "node1"],
1245
      ["node20", "node1"],
1246
      ])
1247
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1248
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1249
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1250
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1251
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1252
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1253
      ["node20", "node1"],
1254
      ["node25", "node1"],
1255
      ["node30", "node90"],
1256
      ["node1", "node44"],
1257
      ])
1258

    
1259
  def testEqualNamesOrder(self):
1260
    fielddefs = query._PrepareFieldList([
1261
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1262
       None, 0, lambda ctx, item: item["pnode"]),
1263
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1264
       None, 0, lambda ctx, item: item["num"]),
1265
      ], [])
1266

    
1267
    data = [
1268
      { "pnode": "node1", "num": 100, },
1269
      { "pnode": "node1", "num": 25, },
1270
      { "pnode": "node2", "num": 90, },
1271
      { "pnode": "node2", "num": 30, },
1272
      ]
1273

    
1274
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1275
                    qfilter=["|", ["=", "pnode", "node1"],
1276
                                  ["=", "pnode", "node2"],
1277
                                  ["=", "pnode", "node1"]])
1278
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1279
                     msg="Did not return unique names")
1280
    self.assertFalse(q.RequestedData())
1281
    self.assertEqual(q.Query(data),
1282
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1283
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1284
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1285
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1286
    self.assertEqual(q.Query(data, sort_by_name=False),
1287
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1288
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1289
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1290
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1291

    
1292
    data = [
1293
      { "pnode": "nodeX", "num": 50, },
1294
      { "pnode": "nodeY", "num": 40, },
1295
      { "pnode": "nodeX", "num": 30, },
1296
      { "pnode": "nodeX", "num": 20, },
1297
      { "pnode": "nodeM", "num": 10, },
1298
      ]
1299

    
1300
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1301
                    qfilter=["|", ["=", "pnode", "nodeX"],
1302
                                  ["=", "pnode", "nodeY"],
1303
                                  ["=", "pnode", "nodeY"],
1304
                                  ["=", "pnode", "nodeY"],
1305
                                  ["=", "pnode", "nodeM"]])
1306
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1307
                     msg="Did not return unique names")
1308
    self.assertFalse(q.RequestedData())
1309

    
1310
    # First sorted by name, then input order
1311
    self.assertEqual(q.Query(data, sort_by_name=True),
1312
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1313
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1314
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1315
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1316
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1317

    
1318
    # Input order
1319
    self.assertEqual(q.Query(data, sort_by_name=False),
1320
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1321
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1322
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1323
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1324
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1325

    
1326
  def testFilter(self):
1327
    (DK_A, DK_B) = range(1000, 1002)
1328

    
1329
    fielddefs = query._PrepareFieldList([
1330
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1331
       DK_A, 0, lambda ctx, item: item["name"]),
1332
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1333
       DK_B, 0, lambda ctx, item: item["other"]),
1334
      ], [])
1335

    
1336
    data = [
1337
      { "name": "node1", "other": "foo", },
1338
      { "name": "node2", "other": "bar", },
1339
      { "name": "node3", "other": "Hello", },
1340
      ]
1341

    
1342
    # Empty filter
1343
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1344
                    qfilter=["|"])
1345
    self.assertTrue(q.RequestedNames() is None)
1346
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1347
    self.assertEqual(q.Query(data), [])
1348

    
1349
    # Normal filter
1350
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1351
                    qfilter=["=", "name", "node1"])
1352
    self.assertEqual(q.RequestedNames(), ["node1"])
1353
    self.assertEqual(q.Query(data),
1354
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1355

    
1356
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1357
                    qfilter=(["|", ["=", "name", "node1"],
1358
                                   ["=", "name", "node3"]]))
1359
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1360
    self.assertEqual(q.Query(data),
1361
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1362
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1363

    
1364
    # Complex filter
1365
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1366
                    qfilter=(["|", ["=", "name", "node1"],
1367
                                   ["|", ["=", "name", "node3"],
1368
                                         ["=", "name", "node2"]],
1369
                                   ["=", "name", "node3"]]))
1370
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1371
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1372
    self.assertEqual(q.Query(data),
1373
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1374
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1375
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1376

    
1377
    # Filter data type mismatch
1378
    for i in [-1, 0, 1, 123, [], None, True, False]:
1379
      self.assertRaises(errors.ParameterError, query.Query,
1380
                        fielddefs, ["name", "other"], namefield="name",
1381
                        qfilter=["=", "name", i])
1382

    
1383
    # Negative filter
1384
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1385
                    qfilter=["!", ["|", ["=", "name", "node1"],
1386
                                        ["=", "name", "node3"]]])
1387
    self.assertTrue(q.RequestedNames() is None)
1388
    self.assertEqual(q.Query(data),
1389
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1390

    
1391
    # Not equal
1392
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1393
                    qfilter=["!=", "name", "node3"])
1394
    self.assertTrue(q.RequestedNames() is None)
1395
    self.assertEqual(q.Query(data),
1396
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1397
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1398

    
1399
    # Data type
1400
    q = query.Query(fielddefs, [], namefield="name",
1401
                    qfilter=["|", ["=", "other", "bar"],
1402
                                  ["=", "name", "foo"]])
1403
    self.assertTrue(q.RequestedNames() is None)
1404
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1405
    self.assertEqual(q.Query(data), [[]])
1406

    
1407
    # Only one data type
1408
    q = query.Query(fielddefs, ["other"], namefield="name",
1409
                    qfilter=["=", "other", "bar"])
1410
    self.assertTrue(q.RequestedNames() is None)
1411
    self.assertEqual(q.RequestedData(), set([DK_B]))
1412
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1413

    
1414
    q = query.Query(fielddefs, [], namefield="name",
1415
                    qfilter=["=", "other", "bar"])
1416
    self.assertTrue(q.RequestedNames() is None)
1417
    self.assertEqual(q.RequestedData(), set([DK_B]))
1418
    self.assertEqual(q.Query(data), [[]])
1419

    
1420
  def testFilterContains(self):
1421
    fielddefs = query._PrepareFieldList([
1422
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1423
       None, 0, lambda ctx, item: item["name"]),
1424
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1425
       None, 0, lambda ctx, item: item["other"]),
1426
      ], [])
1427

    
1428
    data = [
1429
      { "name": "node2", "other": ["x", "y", "bar"], },
1430
      { "name": "node3", "other": "Hello", },
1431
      { "name": "node1", "other": ["a", "b", "foo"], },
1432
      { "name": "empty", "other": []},
1433
      ]
1434

    
1435
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1436
                    qfilter=["=[]", "other", "bar"])
1437
    self.assertTrue(q.RequestedNames() is None)
1438
    self.assertEqual(q.Query(data), [
1439
      [(constants.RS_NORMAL, "node2"),
1440
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1441
      ])
1442

    
1443
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1444
                    qfilter=["|", ["=[]", "other", "bar"],
1445
                                  ["=[]", "other", "a"],
1446
                                  ["=[]", "other", "b"]])
1447
    self.assertTrue(q.RequestedNames() is None)
1448
    self.assertEqual(q.Query(data), [
1449
      [(constants.RS_NORMAL, "node1"),
1450
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1451
      [(constants.RS_NORMAL, "node2"),
1452
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1453
      ])
1454
    self.assertEqual(q.OldStyleQuery(data), [
1455
      ["node1", ["a", "b", "foo"]],
1456
      ["node2", ["x", "y", "bar"]],
1457
      ])
1458

    
1459
    # Boolean test
1460
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1461
                    qfilter=["?", "other"])
1462
    self.assertEqual(q.OldStyleQuery(data), [
1463
      ["node1", ["a", "b", "foo"]],
1464
      ["node2", ["x", "y", "bar"]],
1465
      ["node3", "Hello"],
1466
      ])
1467

    
1468
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1469
                    qfilter=["!", ["?", "other"]])
1470
    self.assertEqual(q.OldStyleQuery(data), [
1471
      ["empty", []],
1472
      ])
1473

    
1474
  def testFilterHostname(self):
1475
    fielddefs = query._PrepareFieldList([
1476
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1477
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1478
      ], [])
1479

    
1480
    data = [
1481
      { "name": "node1.example.com", },
1482
      { "name": "node2.example.com", },
1483
      { "name": "node2.example.net", },
1484
      ]
1485

    
1486
    q = query.Query(fielddefs, ["name"], namefield="name",
1487
                    qfilter=["=", "name", "node2"])
1488
    self.assertEqual(q.RequestedNames(), ["node2"])
1489
    self.assertEqual(q.Query(data), [
1490
      [(constants.RS_NORMAL, "node2.example.com")],
1491
      [(constants.RS_NORMAL, "node2.example.net")],
1492
      ])
1493

    
1494
    q = query.Query(fielddefs, ["name"], namefield="name",
1495
                    qfilter=["=", "name", "node1"])
1496
    self.assertEqual(q.RequestedNames(), ["node1"])
1497
    self.assertEqual(q.Query(data), [
1498
      [(constants.RS_NORMAL, "node1.example.com")],
1499
      ])
1500

    
1501
    q = query.Query(fielddefs, ["name"], namefield="name",
1502
                    qfilter=["=", "name", "othername"])
1503
    self.assertEqual(q.RequestedNames(), ["othername"])
1504
    self.assertEqual(q.Query(data), [])
1505

    
1506
    q = query.Query(fielddefs, ["name"], namefield="name",
1507
                    qfilter=["|", ["=", "name", "node1.example.com"],
1508
                                  ["=", "name", "node2"]])
1509
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1510
    self.assertEqual(q.Query(data), [
1511
      [(constants.RS_NORMAL, "node1.example.com")],
1512
      [(constants.RS_NORMAL, "node2.example.com")],
1513
      [(constants.RS_NORMAL, "node2.example.net")],
1514
      ])
1515
    self.assertEqual(q.OldStyleQuery(data), [
1516
      ["node1.example.com"],
1517
      ["node2.example.com"],
1518
      ["node2.example.net"],
1519
      ])
1520

    
1521
    q = query.Query(fielddefs, ["name"], namefield="name",
1522
                    qfilter=["!=", "name", "node1"])
1523
    self.assertTrue(q.RequestedNames() is None)
1524
    self.assertEqual(q.Query(data), [
1525
      [(constants.RS_NORMAL, "node2.example.com")],
1526
      [(constants.RS_NORMAL, "node2.example.net")],
1527
      ])
1528
    self.assertEqual(q.OldStyleQuery(data), [
1529
      ["node2.example.com"],
1530
      ["node2.example.net"],
1531
      ])
1532

    
1533
  def testFilterBoolean(self):
1534
    fielddefs = query._PrepareFieldList([
1535
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1536
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1537
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1538
       None, 0, lambda ctx, item: item["value"]),
1539
      ], [])
1540

    
1541
    data = [
1542
      { "name": "node1", "value": False, },
1543
      { "name": "node2", "value": True, },
1544
      { "name": "node3", "value": True, },
1545
      ]
1546

    
1547
    q = query.Query(fielddefs, ["name", "value"],
1548
                    qfilter=["|", ["=", "value", False],
1549
                                  ["=", "value", True]])
1550
    self.assertTrue(q.RequestedNames() is None)
1551
    self.assertEqual(q.Query(data), [
1552
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1553
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1554
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1555
      ])
1556

    
1557
    q = query.Query(fielddefs, ["name", "value"],
1558
                    qfilter=["|", ["=", "value", False],
1559
                                  ["!", ["=", "value", False]]])
1560
    self.assertTrue(q.RequestedNames() is None)
1561
    self.assertEqual(q.Query(data), [
1562
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1563
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1564
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1565
      ])
1566

    
1567
    # Comparing bool with string
1568
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1569
      self.assertRaises(errors.ParameterError, query.Query,
1570
                        fielddefs, ["name", "value"],
1571
                        qfilter=["=", "value", i])
1572

    
1573
    # Truth filter
1574
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1575
    self.assertTrue(q.RequestedNames() is None)
1576
    self.assertEqual(q.Query(data), [
1577
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1578
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1579
      ])
1580

    
1581
    # Negative bool filter
1582
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1583
    self.assertTrue(q.RequestedNames() is None)
1584
    self.assertEqual(q.Query(data), [
1585
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1586
      ])
1587

    
1588
    # Complex truth filter
1589
    q = query.Query(fielddefs, ["name", "value"],
1590
                    qfilter=["|", ["&", ["=", "name", "node1"],
1591
                                        ["!", ["?", "value"]]],
1592
                                  ["?", "value"]])
1593
    self.assertTrue(q.RequestedNames() is None)
1594
    self.assertEqual(q.Query(data), [
1595
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1596
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1597
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1598
      ])
1599

    
1600
  def testFilterRegex(self):
1601
    fielddefs = query._PrepareFieldList([
1602
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1603
       None, 0, lambda ctx, item: item["name"]),
1604
      ], [])
1605

    
1606
    data = [
1607
      { "name": "node1.example.com", },
1608
      { "name": "node2.site.example.com", },
1609
      { "name": "node2.example.net", },
1610

    
1611
      # Empty name
1612
      { "name": "", },
1613
      ]
1614

    
1615
    q = query.Query(fielddefs, ["name"], namefield="name",
1616
                    qfilter=["=~", "name", "site"])
1617
    self.assertTrue(q.RequestedNames() is None)
1618
    self.assertEqual(q.Query(data), [
1619
      [(constants.RS_NORMAL, "node2.site.example.com")],
1620
      ])
1621

    
1622
    q = query.Query(fielddefs, ["name"], namefield="name",
1623
                    qfilter=["=~", "name", "^node2"])
1624
    self.assertTrue(q.RequestedNames() is None)
1625
    self.assertEqual(q.Query(data), [
1626
      [(constants.RS_NORMAL, "node2.example.net")],
1627
      [(constants.RS_NORMAL, "node2.site.example.com")],
1628
      ])
1629

    
1630
    q = query.Query(fielddefs, ["name"], namefield="name",
1631
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1632
    self.assertTrue(q.RequestedNames() is None)
1633
    self.assertEqual(q.Query(data), [
1634
      [(constants.RS_NORMAL, "node1.example.com")],
1635
      [(constants.RS_NORMAL, "node2.site.example.com")],
1636
      ])
1637

    
1638
    q = query.Query(fielddefs, ["name"], namefield="name",
1639
                    qfilter=["=~", "name", r"."])
1640
    self.assertTrue(q.RequestedNames() is None)
1641
    self.assertEqual(q.Query(data), [
1642
      [(constants.RS_NORMAL, "node1.example.com")],
1643
      [(constants.RS_NORMAL, "node2.example.net")],
1644
      [(constants.RS_NORMAL, "node2.site.example.com")],
1645
      ])
1646

    
1647
    q = query.Query(fielddefs, ["name"], namefield="name",
1648
                    qfilter=["=~", "name", r"^$"])
1649
    self.assertTrue(q.RequestedNames() is None)
1650
    self.assertEqual(q.Query(data), [
1651
      [(constants.RS_NORMAL, "")],
1652
      ])
1653

    
1654
    # Invalid regular expression
1655
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1656
                      qfilter=["=~", "name", r"["])
1657

    
1658

    
1659
if __name__ == "__main__":
1660
  testutils.GanetiTestProgram()