Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 0f63f1f9

History | View | Annotate | Download (64.9 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
                        ipolicy=objects.MakeEmptyIPolicy()),
937
      objects.NodeGroup(name="restricted",
938
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
939
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
940
                        ipolicy=objects.MakeEmptyIPolicy()),
941
      ]
942
    self.cluster = objects.Cluster(cluster_name="testcluster",
943
      hvparams=constants.HVC_DEFAULTS,
944
      beparams={
945
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
946
        },
947
      nicparams={
948
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
949
        },
950
      ndparams=constants.NDC_DEFAULTS,
951
      ipolicy=constants.IPOLICY_DEFAULTS,
952
      )
953

    
954
  def _Create(self, selected):
955
    return query.Query(query.GROUP_FIELDS, selected)
956

    
957
  def testSimple(self):
958
    q = self._Create(["name", "uuid", "alloc_policy"])
959
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None)
960

    
961
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
962

    
963
    self.assertEqual(q.Query(gqd),
964
      [[(constants.RS_NORMAL, "default"),
965
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
966
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
967
        ],
968
       [(constants.RS_NORMAL, "restricted"),
969
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
970
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
971
        ],
972
       ])
973

    
974
  def testNodes(self):
975
    groups_to_nodes = {
976
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
977
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
978
      }
979

    
980
    q = self._Create(["name", "node_cnt", "node_list"])
981
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None)
982

    
983
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
984

    
985
    self.assertEqual(q.Query(gqd),
986
                     [[(constants.RS_NORMAL, "default"),
987
                       (constants.RS_NORMAL, 2),
988
                       (constants.RS_NORMAL, ["node1", "node2"]),
989
                       ],
990
                      [(constants.RS_NORMAL, "restricted"),
991
                       (constants.RS_NORMAL, 3),
992
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
993
                       ],
994
                      ])
995

    
996
  def testInstances(self):
997
    groups_to_instances = {
998
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
999
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1000
      }
1001

    
1002
    q = self._Create(["pinst_cnt", "pinst_list"])
1003
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1004
      groups_to_instances)
1005

    
1006
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1007

    
1008
    self.assertEqual(q.Query(gqd),
1009
                     [[(constants.RS_NORMAL, 2),
1010
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1011
                       ],
1012
                      [(constants.RS_NORMAL, 3),
1013
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1014
                       ],
1015
                      ])
1016

    
1017

    
1018
class TestOsQuery(unittest.TestCase):
1019
  def _Create(self, selected):
1020
    return query.Query(query.OS_FIELDS, selected)
1021

    
1022
  def test(self):
1023
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1024
    api_versions = [10, 0, 15, 5]
1025
    parameters = ["zpar3", "apar9"]
1026

    
1027
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1028
    assert (api_versions != sorted(api_versions) and
1029
            api_versions != utils.NiceSort(variants))
1030
    assert (parameters != sorted(parameters) and
1031
            parameters != utils.NiceSort(parameters))
1032

    
1033
    data = [
1034
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1035
                   variants=set(), api_versions=set(), parameters=set(),
1036
                   node_status={ "some": "status", }),
1037
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1038
                   variants=set(variants),
1039
                   api_versions=set(api_versions),
1040
                   parameters=set(parameters),
1041
                   node_status={ "some": "other", "status": None, }),
1042
      ]
1043

    
1044

    
1045
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1046
                      "api_versions", "parameters", "node_status"])
1047
    self.assertEqual(q.RequestedData(), set([]))
1048
    self.assertEqual(q.Query(data),
1049
                     [[(constants.RS_NORMAL, "debian"),
1050
                       (constants.RS_NORMAL, False),
1051
                       (constants.RS_NORMAL, False),
1052
                       (constants.RS_NORMAL, False),
1053
                       (constants.RS_NORMAL, []),
1054
                       (constants.RS_NORMAL, []),
1055
                       (constants.RS_NORMAL, []),
1056
                       (constants.RS_NORMAL, {"some": "status"})],
1057
                      [(constants.RS_NORMAL, "dos"),
1058
                       (constants.RS_NORMAL, True),
1059
                       (constants.RS_NORMAL, False),
1060
                       (constants.RS_NORMAL, True),
1061
                       (constants.RS_NORMAL,
1062
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1063
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1064
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1065
                       (constants.RS_NORMAL,
1066
                        { "some": "other", "status": None, })
1067
                       ]])
1068

    
1069

    
1070
class TestQueryFields(unittest.TestCase):
1071
  def testAllFields(self):
1072
    for fielddefs in query.ALL_FIELD_LISTS:
1073
      result = query.QueryFields(fielddefs, None)
1074
      self.assert_(isinstance(result, dict))
1075
      response = objects.QueryFieldsResponse.FromDict(result)
1076
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1077
        [(fdef2.name, fdef2.title)
1078
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1079
                                                key=lambda x: x[0].name)])
1080

    
1081
  def testSomeFields(self):
1082
    rnd = random.Random(5357)
1083

    
1084
    for _ in range(10):
1085
      for fielddefs in query.ALL_FIELD_LISTS:
1086
        if len(fielddefs) > 20:
1087
          sample_size = rnd.randint(5, 20)
1088
        else:
1089
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1090
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1091
                                                         sample_size)]
1092
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1093
        self.assert_(isinstance(result, dict))
1094
        response = objects.QueryFieldsResponse.FromDict(result)
1095
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1096
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1097

    
1098

    
1099
class TestQueryFilter(unittest.TestCase):
1100
  def testRequestedNames(self):
1101
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1102

    
1103
    for fielddefs in query.ALL_FIELD_LISTS:
1104
      assert "name" in fielddefs
1105

    
1106
      # No name field
1107
      q = query.Query(fielddefs, ["name"], qfilter=["=", "name", "abc"],
1108
                      namefield=None)
1109
      self.assertEqual(q.RequestedNames(), None)
1110

    
1111
      # No filter
1112
      q = query.Query(fielddefs, ["name"], qfilter=None, namefield="name")
1113
      self.assertEqual(q.RequestedNames(), None)
1114

    
1115
      # Check empty query
1116
      q = query.Query(fielddefs, ["name"], qfilter=["|"], namefield="name")
1117
      self.assertEqual(q.RequestedNames(), None)
1118

    
1119
      # Check order
1120
      q = query.Query(fielddefs, ["name"], qfilter=["|"] + innerfilter,
1121
                      namefield="name")
1122
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1123

    
1124
      # Check reverse order
1125
      q = query.Query(fielddefs, ["name"],
1126
                      qfilter=["|"] + list(reversed(innerfilter)),
1127
                      namefield="name")
1128
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1129

    
1130
      # Duplicates
1131
      q = query.Query(fielddefs, ["name"],
1132
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1133
                      namefield="name")
1134
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1135

    
1136
      # Unknown name field
1137
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1138
                        namefield="_unknown_field_")
1139

    
1140
      # Filter with AND
1141
      q = query.Query(fielddefs, ["name"],
1142
                      qfilter=["|", ["=", "name", "foo"],
1143
                                    ["&", ["=", "name", ""]]],
1144
                      namefield="name")
1145
      self.assertTrue(q.RequestedNames() is None)
1146

    
1147
      # Filter with NOT
1148
      q = query.Query(fielddefs, ["name"],
1149
                      qfilter=["|", ["=", "name", "foo"],
1150
                                    ["!", ["=", "name", ""]]],
1151
                      namefield="name")
1152
      self.assertTrue(q.RequestedNames() is None)
1153

    
1154
      # Filter with only OR (names must be in correct order)
1155
      q = query.Query(fielddefs, ["name"],
1156
                      qfilter=["|", ["=", "name", "x17361"],
1157
                                    ["|", ["=", "name", "x22015"]],
1158
                                    ["|", ["|", ["=", "name", "x13193"]]],
1159
                                    ["=", "name", "x15215"]],
1160
                      namefield="name")
1161
      self.assertEqual(q.RequestedNames(),
1162
                       ["x17361", "x22015", "x13193", "x15215"])
1163

    
1164
  @staticmethod
1165
  def _GenNestedFilter(op, depth):
1166
    nested = ["=", "name", "value"]
1167
    for i in range(depth):
1168
      nested = [op, nested]
1169
    return nested
1170

    
1171
  def testCompileFilter(self):
1172
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1173

    
1174
    checks = [
1175
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1176
      ["=", "_unknown_field", "value"],
1177
      self._GenNestedFilter("|", levels_max),
1178
      self._GenNestedFilter("|", levels_max * 3),
1179
      self._GenNestedFilter("!", levels_max),
1180
      ]
1181

    
1182
    for fielddefs in query.ALL_FIELD_LISTS:
1183
      for qfilter in checks:
1184
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1185
                          fielddefs, None, qfilter)
1186

    
1187
      for op in ["|", "!"]:
1188
        qfilter = self._GenNestedFilter(op, levels_max - 1)
1189
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1190
                                                      qfilter)))
1191

    
1192
  def testQueryInputOrder(self):
1193
    fielddefs = query._PrepareFieldList([
1194
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1195
       None, 0, lambda ctx, item: item["pnode"]),
1196
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1197
       None, 0, lambda ctx, item: item["snode"]),
1198
      ], [])
1199

    
1200
    data = [
1201
      { "pnode": "node1", "snode": "node44", },
1202
      { "pnode": "node30", "snode": "node90", },
1203
      { "pnode": "node25", "snode": "node1", },
1204
      { "pnode": "node20", "snode": "node1", },
1205
      ]
1206

    
1207
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1208

    
1209
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1210
                    qfilter=qfilter)
1211
    self.assertTrue(q.RequestedNames() is None)
1212
    self.assertFalse(q.RequestedData())
1213
    self.assertEqual(q.Query(data),
1214
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1215
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1216
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1217

    
1218
    # Try again with reversed input data
1219
    self.assertEqual(q.Query(reversed(data)),
1220
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1221
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1222
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1223

    
1224
    # No name field, result must be in incoming order
1225
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1226
                    qfilter=qfilter)
1227
    self.assertFalse(q.RequestedData())
1228
    self.assertEqual(q.Query(data),
1229
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1230
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1231
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1232
    self.assertEqual(q.OldStyleQuery(data), [
1233
      ["node1", "node44"],
1234
      ["node25", "node1"],
1235
      ["node20", "node1"],
1236
      ])
1237
    self.assertEqual(q.Query(reversed(data)),
1238
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1239
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1240
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1241
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1242
      ["node20", "node1"],
1243
      ["node25", "node1"],
1244
      ["node1", "node44"],
1245
      ])
1246

    
1247
    # Name field, but no sorting, result must be in incoming order
1248
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1249
    self.assertFalse(q.RequestedData())
1250
    self.assertEqual(q.Query(data, sort_by_name=False),
1251
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1252
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1253
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1254
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1255
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1256
      ["node1", "node44"],
1257
      ["node30", "node90"],
1258
      ["node25", "node1"],
1259
      ["node20", "node1"],
1260
      ])
1261
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1262
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1263
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1264
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1265
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1266
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1267
      ["node20", "node1"],
1268
      ["node25", "node1"],
1269
      ["node30", "node90"],
1270
      ["node1", "node44"],
1271
      ])
1272

    
1273
  def testEqualNamesOrder(self):
1274
    fielddefs = query._PrepareFieldList([
1275
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1276
       None, 0, lambda ctx, item: item["pnode"]),
1277
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1278
       None, 0, lambda ctx, item: item["num"]),
1279
      ], [])
1280

    
1281
    data = [
1282
      { "pnode": "node1", "num": 100, },
1283
      { "pnode": "node1", "num": 25, },
1284
      { "pnode": "node2", "num": 90, },
1285
      { "pnode": "node2", "num": 30, },
1286
      ]
1287

    
1288
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1289
                    qfilter=["|", ["=", "pnode", "node1"],
1290
                                  ["=", "pnode", "node2"],
1291
                                  ["=", "pnode", "node1"]])
1292
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1293
                     msg="Did not return unique names")
1294
    self.assertFalse(q.RequestedData())
1295
    self.assertEqual(q.Query(data),
1296
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1297
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1298
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1299
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1300
    self.assertEqual(q.Query(data, sort_by_name=False),
1301
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1302
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1303
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1304
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1305

    
1306
    data = [
1307
      { "pnode": "nodeX", "num": 50, },
1308
      { "pnode": "nodeY", "num": 40, },
1309
      { "pnode": "nodeX", "num": 30, },
1310
      { "pnode": "nodeX", "num": 20, },
1311
      { "pnode": "nodeM", "num": 10, },
1312
      ]
1313

    
1314
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1315
                    qfilter=["|", ["=", "pnode", "nodeX"],
1316
                                  ["=", "pnode", "nodeY"],
1317
                                  ["=", "pnode", "nodeY"],
1318
                                  ["=", "pnode", "nodeY"],
1319
                                  ["=", "pnode", "nodeM"]])
1320
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1321
                     msg="Did not return unique names")
1322
    self.assertFalse(q.RequestedData())
1323

    
1324
    # First sorted by name, then input order
1325
    self.assertEqual(q.Query(data, sort_by_name=True),
1326
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1327
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1328
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1329
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1330
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1331

    
1332
    # Input order
1333
    self.assertEqual(q.Query(data, sort_by_name=False),
1334
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1335
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1336
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1337
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1338
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1339

    
1340
  def testFilter(self):
1341
    (DK_A, DK_B) = range(1000, 1002)
1342

    
1343
    fielddefs = query._PrepareFieldList([
1344
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1345
       DK_A, 0, lambda ctx, item: item["name"]),
1346
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1347
       DK_B, 0, lambda ctx, item: item["other"]),
1348
      ], [])
1349

    
1350
    data = [
1351
      { "name": "node1", "other": "foo", },
1352
      { "name": "node2", "other": "bar", },
1353
      { "name": "node3", "other": "Hello", },
1354
      ]
1355

    
1356
    # Empty filter
1357
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1358
                    qfilter=["|"])
1359
    self.assertTrue(q.RequestedNames() is None)
1360
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1361
    self.assertEqual(q.Query(data), [])
1362

    
1363
    # Normal filter
1364
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1365
                    qfilter=["=", "name", "node1"])
1366
    self.assertEqual(q.RequestedNames(), ["node1"])
1367
    self.assertEqual(q.Query(data),
1368
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1369

    
1370
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1371
                    qfilter=(["|", ["=", "name", "node1"],
1372
                                   ["=", "name", "node3"]]))
1373
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1374
    self.assertEqual(q.Query(data),
1375
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1376
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1377

    
1378
    # Complex filter
1379
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1380
                    qfilter=(["|", ["=", "name", "node1"],
1381
                                   ["|", ["=", "name", "node3"],
1382
                                         ["=", "name", "node2"]],
1383
                                   ["=", "name", "node3"]]))
1384
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1385
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1386
    self.assertEqual(q.Query(data),
1387
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1388
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1389
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1390

    
1391
    # Filter data type mismatch
1392
    for i in [-1, 0, 1, 123, [], None, True, False]:
1393
      self.assertRaises(errors.ParameterError, query.Query,
1394
                        fielddefs, ["name", "other"], namefield="name",
1395
                        qfilter=["=", "name", i])
1396

    
1397
    # Negative filter
1398
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1399
                    qfilter=["!", ["|", ["=", "name", "node1"],
1400
                                        ["=", "name", "node3"]]])
1401
    self.assertTrue(q.RequestedNames() is None)
1402
    self.assertEqual(q.Query(data),
1403
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1404

    
1405
    # Not equal
1406
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1407
                    qfilter=["!=", "name", "node3"])
1408
    self.assertTrue(q.RequestedNames() is None)
1409
    self.assertEqual(q.Query(data),
1410
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1411
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1412

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

    
1421
    # Only one data type
1422
    q = query.Query(fielddefs, ["other"], namefield="name",
1423
                    qfilter=["=", "other", "bar"])
1424
    self.assertTrue(q.RequestedNames() is None)
1425
    self.assertEqual(q.RequestedData(), set([DK_B]))
1426
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1427

    
1428
    q = query.Query(fielddefs, [], namefield="name",
1429
                    qfilter=["=", "other", "bar"])
1430
    self.assertTrue(q.RequestedNames() is None)
1431
    self.assertEqual(q.RequestedData(), set([DK_B]))
1432
    self.assertEqual(q.Query(data), [[]])
1433

    
1434
  def testFilterContains(self):
1435
    fielddefs = query._PrepareFieldList([
1436
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1437
       None, 0, lambda ctx, item: item["name"]),
1438
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1439
       None, 0, lambda ctx, item: item["other"]),
1440
      ], [])
1441

    
1442
    data = [
1443
      { "name": "node2", "other": ["x", "y", "bar"], },
1444
      { "name": "node3", "other": "Hello", },
1445
      { "name": "node1", "other": ["a", "b", "foo"], },
1446
      { "name": "empty", "other": []},
1447
      ]
1448

    
1449
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1450
                    qfilter=["=[]", "other", "bar"])
1451
    self.assertTrue(q.RequestedNames() is None)
1452
    self.assertEqual(q.Query(data), [
1453
      [(constants.RS_NORMAL, "node2"),
1454
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1455
      ])
1456

    
1457
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1458
                    qfilter=["|", ["=[]", "other", "bar"],
1459
                                  ["=[]", "other", "a"],
1460
                                  ["=[]", "other", "b"]])
1461
    self.assertTrue(q.RequestedNames() is None)
1462
    self.assertEqual(q.Query(data), [
1463
      [(constants.RS_NORMAL, "node1"),
1464
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1465
      [(constants.RS_NORMAL, "node2"),
1466
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1467
      ])
1468
    self.assertEqual(q.OldStyleQuery(data), [
1469
      ["node1", ["a", "b", "foo"]],
1470
      ["node2", ["x", "y", "bar"]],
1471
      ])
1472

    
1473
    # Boolean test
1474
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1475
                    qfilter=["?", "other"])
1476
    self.assertEqual(q.OldStyleQuery(data), [
1477
      ["node1", ["a", "b", "foo"]],
1478
      ["node2", ["x", "y", "bar"]],
1479
      ["node3", "Hello"],
1480
      ])
1481

    
1482
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1483
                    qfilter=["!", ["?", "other"]])
1484
    self.assertEqual(q.OldStyleQuery(data), [
1485
      ["empty", []],
1486
      ])
1487

    
1488
  def testFilterHostname(self):
1489
    fielddefs = query._PrepareFieldList([
1490
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1491
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1492
      ], [])
1493

    
1494
    data = [
1495
      { "name": "node1.example.com", },
1496
      { "name": "node2.example.com", },
1497
      { "name": "node2.example.net", },
1498
      ]
1499

    
1500
    q = query.Query(fielddefs, ["name"], namefield="name",
1501
                    qfilter=["=", "name", "node2"])
1502
    self.assertEqual(q.RequestedNames(), ["node2"])
1503
    self.assertEqual(q.Query(data), [
1504
      [(constants.RS_NORMAL, "node2.example.com")],
1505
      [(constants.RS_NORMAL, "node2.example.net")],
1506
      ])
1507

    
1508
    q = query.Query(fielddefs, ["name"], namefield="name",
1509
                    qfilter=["=", "name", "node1"])
1510
    self.assertEqual(q.RequestedNames(), ["node1"])
1511
    self.assertEqual(q.Query(data), [
1512
      [(constants.RS_NORMAL, "node1.example.com")],
1513
      ])
1514

    
1515
    q = query.Query(fielddefs, ["name"], namefield="name",
1516
                    qfilter=["=", "name", "othername"])
1517
    self.assertEqual(q.RequestedNames(), ["othername"])
1518
    self.assertEqual(q.Query(data), [])
1519

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

    
1535
    q = query.Query(fielddefs, ["name"], namefield="name",
1536
                    qfilter=["!=", "name", "node1"])
1537
    self.assertTrue(q.RequestedNames() is None)
1538
    self.assertEqual(q.Query(data), [
1539
      [(constants.RS_NORMAL, "node2.example.com")],
1540
      [(constants.RS_NORMAL, "node2.example.net")],
1541
      ])
1542
    self.assertEqual(q.OldStyleQuery(data), [
1543
      ["node2.example.com"],
1544
      ["node2.example.net"],
1545
      ])
1546

    
1547
  def testFilterBoolean(self):
1548
    fielddefs = query._PrepareFieldList([
1549
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1550
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1551
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1552
       None, 0, lambda ctx, item: item["value"]),
1553
      ], [])
1554

    
1555
    data = [
1556
      { "name": "node1", "value": False, },
1557
      { "name": "node2", "value": True, },
1558
      { "name": "node3", "value": True, },
1559
      ]
1560

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

    
1571
    q = query.Query(fielddefs, ["name", "value"],
1572
                    qfilter=["|", ["=", "value", False],
1573
                                  ["!", ["=", "value", False]]])
1574
    self.assertTrue(q.RequestedNames() is None)
1575
    self.assertEqual(q.Query(data), [
1576
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1577
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1578
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1579
      ])
1580

    
1581
    # Comparing bool with string
1582
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1583
      self.assertRaises(errors.ParameterError, query.Query,
1584
                        fielddefs, ["name", "value"],
1585
                        qfilter=["=", "value", i])
1586

    
1587
    # Truth filter
1588
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1589
    self.assertTrue(q.RequestedNames() is None)
1590
    self.assertEqual(q.Query(data), [
1591
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1592
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1593
      ])
1594

    
1595
    # Negative bool filter
1596
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1597
    self.assertTrue(q.RequestedNames() is None)
1598
    self.assertEqual(q.Query(data), [
1599
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1600
      ])
1601

    
1602
    # Complex truth filter
1603
    q = query.Query(fielddefs, ["name", "value"],
1604
                    qfilter=["|", ["&", ["=", "name", "node1"],
1605
                                        ["!", ["?", "value"]]],
1606
                                  ["?", "value"]])
1607
    self.assertTrue(q.RequestedNames() is None)
1608
    self.assertEqual(q.Query(data), [
1609
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1610
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1611
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1612
      ])
1613

    
1614
  def testFilterRegex(self):
1615
    fielddefs = query._PrepareFieldList([
1616
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1617
       None, 0, lambda ctx, item: item["name"]),
1618
      ], [])
1619

    
1620
    data = [
1621
      { "name": "node1.example.com", },
1622
      { "name": "node2.site.example.com", },
1623
      { "name": "node2.example.net", },
1624

    
1625
      # Empty name
1626
      { "name": "", },
1627
      ]
1628

    
1629
    q = query.Query(fielddefs, ["name"], namefield="name",
1630
                    qfilter=["=~", "name", "site"])
1631
    self.assertTrue(q.RequestedNames() is None)
1632
    self.assertEqual(q.Query(data), [
1633
      [(constants.RS_NORMAL, "node2.site.example.com")],
1634
      ])
1635

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

    
1644
    q = query.Query(fielddefs, ["name"], namefield="name",
1645
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1646
    self.assertTrue(q.RequestedNames() is None)
1647
    self.assertEqual(q.Query(data), [
1648
      [(constants.RS_NORMAL, "node1.example.com")],
1649
      [(constants.RS_NORMAL, "node2.site.example.com")],
1650
      ])
1651

    
1652
    q = query.Query(fielddefs, ["name"], namefield="name",
1653
                    qfilter=["=~", "name", r"."])
1654
    self.assertTrue(q.RequestedNames() is None)
1655
    self.assertEqual(q.Query(data), [
1656
      [(constants.RS_NORMAL, "node1.example.com")],
1657
      [(constants.RS_NORMAL, "node2.example.net")],
1658
      [(constants.RS_NORMAL, "node2.site.example.com")],
1659
      ])
1660

    
1661
    q = query.Query(fielddefs, ["name"], namefield="name",
1662
                    qfilter=["=~", "name", r"^$"])
1663
    self.assertTrue(q.RequestedNames() is None)
1664
    self.assertEqual(q.Query(data), [
1665
      [(constants.RS_NORMAL, "")],
1666
      ])
1667

    
1668
    # Invalid regular expression
1669
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1670
                      qfilter=["=~", "name", r"["])
1671

    
1672

    
1673
if __name__ == "__main__":
1674
  testutils.GanetiTestProgram()