Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 9ca8a7c5

History | View | Annotate | Download (63.6 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/memory", "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_MEMORY: 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=True, hypervisor=constants.HT_XEN_PVM, os="linux1",
648
        primary_node="node1",
649
        disk_template=constants.DT_PLAIN,
650
        disks=[],
651
        osparams={}),
652
      objects.Instance(name="inst2", hvparams={}, nics=[],
653
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
654
        ctime=1291211000, mtime=1291211077, serial_no=1,
655
        admin_state=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
656
        primary_node="node5",
657
        disk_template=constants.DT_DISKLESS,
658
        disks=[],
659
        beparams={
660
          constants.BE_MEMORY: 512,
661
        },
662
        osparams={}),
663
      objects.Instance(name="inst3", hvparams={}, beparams={},
664
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
665
        ctime=1291011000, mtime=1291013000, serial_no=1923,
666
        admin_state=False, hypervisor=constants.HT_KVM, os="busybox",
667
        primary_node="node6",
668
        disk_template=constants.DT_DRBD8,
669
        disks=[],
670
        nics=[
671
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
672
                      nicparams={
673
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
674
                        }),
675
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
676
          ],
677
        osparams={}),
678
      objects.Instance(name="inst4", hvparams={}, beparams={},
679
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
680
        ctime=1291244390, mtime=1291244395, serial_no=25,
681
        admin_state=False, hypervisor=constants.HT_XEN_PVM, os="linux1",
682
        primary_node="nodeoff2",
683
        disk_template=constants.DT_DRBD8,
684
        disks=[],
685
        nics=[
686
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
687
                      nicparams={
688
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
689
                        }),
690
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
691
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
692
                      nicparams={
693
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
694
                        }),
695
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
696
                      nicparams={
697
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
698
                        constants.NIC_LINK: "eth123",
699
                        }),
700
          ],
701
        osparams={}),
702
      objects.Instance(name="inst5", hvparams={}, nics=[],
703
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
704
        ctime=1231211000, mtime=1261200000, serial_no=3,
705
        admin_state=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
706
        primary_node="nodebad2",
707
        disk_template=constants.DT_DISKLESS,
708
        disks=[],
709
        beparams={
710
          constants.BE_MEMORY: 512,
711
        },
712
        osparams={}),
713
      objects.Instance(name="inst6", hvparams={}, nics=[],
714
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
715
        ctime=7513, mtime=11501, serial_no=13390,
716
        admin_state=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
717
        primary_node="node7",
718
        disk_template=constants.DT_DISKLESS,
719
        disks=[],
720
        beparams={
721
          constants.BE_MEMORY: 768,
722
        },
723
        osparams={
724
          "clean_install": "no",
725
          }),
726
      objects.Instance(name="inst7", hvparams={}, nics=[],
727
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
728
        ctime=None, mtime=None, serial_no=1947,
729
        admin_state=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
730
        primary_node="node6",
731
        disk_template=constants.DT_DISKLESS,
732
        disks=[],
733
        beparams={},
734
        osparams={}),
735
      ]
736

    
737
    assert not utils.FindDuplicates(inst.name for inst in instances)
738

    
739
    instbyname = dict((inst.name, inst) for inst in instances)
740

    
741
    disk_usage = dict((inst.name,
742
                       cmdlib._ComputeDiskSize(inst.disk_template,
743
                                               [{"size": disk.size}
744
                                                for disk in inst.disks]))
745
                      for inst in instances)
746

    
747
    inst_bridges = {
748
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
749
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
750
                None, "eth123"],
751
      }
752

    
753
    live_data = {
754
      "inst2": {
755
        "vcpus": 3,
756
        },
757
      "inst4": {
758
        "memory": 123,
759
        },
760
      "inst6": {
761
        "memory": 768,
762
        },
763
      "inst7": {
764
        "vcpus": 3,
765
        },
766
      }
767
    wrongnode_inst = set(["inst7"])
768

    
769
    consinfo = dict((inst.name, None) for inst in instances)
770
    consinfo["inst7"] = \
771
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
772
                              host=instbyname["inst7"].primary_node,
773
                              user=constants.GANETI_RUNAS,
774
                              command=["hostname"]).ToDict()
775

    
776
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
777
                                  offline_nodes, bad_nodes, live_data,
778
                                  wrongnode_inst, consinfo, {}, {})
779
    result = q.Query(iqd)
780
    self.assertEqual(len(result), len(instances))
781
    self.assert_(compat.all(len(row) == len(selected)
782
                            for row in result))
783

    
784
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
785
           "Offline nodes not included in bad nodes"
786

    
787
    tested_status = set()
788

    
789
    for (inst, row) in zip(instances, result):
790
      assert inst.primary_node in nodes
791

    
792
      self.assertEqual(row[fieldidx["name"]],
793
                       (constants.RS_NORMAL, inst.name))
794

    
795
      if inst.primary_node in offline_nodes:
796
        exp_status = constants.INSTST_NODEOFFLINE
797
      elif inst.primary_node in bad_nodes:
798
        exp_status = constants.INSTST_NODEDOWN
799
      elif inst.name in live_data:
800
        if inst.name in wrongnode_inst:
801
          exp_status = constants.INSTST_WRONGNODE
802
        elif inst.admin_state:
803
          exp_status = constants.INSTST_RUNNING
804
        else:
805
          exp_status = constants.INSTST_ERRORUP
806
      elif inst.admin_state:
807
        exp_status = constants.INSTST_ERRORDOWN
808
      else:
809
        exp_status = constants.INSTST_ADMINDOWN
810

    
811
      self.assertEqual(row[fieldidx["status"]],
812
                       (constants.RS_NORMAL, exp_status))
813

    
814
      (_, status) = row[fieldidx["status"]]
815
      tested_status.add(status)
816

    
817
      for (field, livefield) in [("oper_ram", "memory"),
818
                                 ("oper_vcpus", "vcpus")]:
819
        if inst.primary_node in bad_nodes:
820
          exp = (constants.RS_NODATA, None)
821
        elif inst.name in live_data:
822
          value = live_data[inst.name].get(livefield, None)
823
          if value is None:
824
            exp = (constants.RS_UNAVAIL, None)
825
          else:
826
            exp = (constants.RS_NORMAL, value)
827
        else:
828
          exp = (constants.RS_UNAVAIL, None)
829

    
830
        self.assertEqual(row[fieldidx[field]], exp)
831

    
832
      bridges = inst_bridges.get(inst.name, [])
833
      self.assertEqual(row[fieldidx["nic.bridges"]],
834
                       (constants.RS_NORMAL, bridges))
835
      if bridges:
836
        self.assertEqual(row[fieldidx["bridge"]],
837
                         (constants.RS_NORMAL, bridges[0]))
838
      else:
839
        self.assertEqual(row[fieldidx["bridge"]],
840
                         (constants.RS_UNAVAIL, None))
841

    
842
      for i in range(constants.MAX_NICS):
843
        if i < len(bridges) and bridges[i] is not None:
844
          exp = (constants.RS_NORMAL, bridges[i])
845
        else:
846
          exp = (constants.RS_UNAVAIL, None)
847
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
848

    
849
      if inst.primary_node in bad_nodes:
850
        exp = (constants.RS_NODATA, None)
851
      else:
852
        exp = (constants.RS_NORMAL, inst.name in live_data)
853
      self.assertEqual(row[fieldidx["oper_state"]], exp)
854

    
855
      cust_exp = (constants.RS_NORMAL, {})
856
      if inst.os == "deb99":
857
        if inst.name == "inst6":
858
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
859
          cust_exp = exp
860
        else:
861
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
862
      else:
863
        exp = (constants.RS_NORMAL, {})
864
      self.assertEqual(row[fieldidx["osparams"]], exp)
865
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
866

    
867
      usage = disk_usage[inst.name]
868
      if usage is None:
869
        usage = 0
870
      self.assertEqual(row[fieldidx["disk_usage"]],
871
                       (constants.RS_NORMAL, usage))
872

    
873
      for alias, target in [("sda_size", "disk.size/0"),
874
                            ("sdb_size", "disk.size/1"),
875
                            ("vcpus", "be/vcpus"),
876
                            ("ip", "nic.ip/0"),
877
                            ("mac", "nic.mac/0"),
878
                            ("bridge", "nic.bridge/0"),
879
                            ("nic_mode", "nic.mode/0"),
880
                            ("nic_link", "nic.link/0"),
881
                            ]:
882
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
883

    
884
      for field in ["ctime", "mtime"]:
885
        if getattr(inst, field) is None:
886
          # No ctime/mtime
887
          exp = (constants.RS_UNAVAIL, None)
888
        else:
889
          exp = (constants.RS_NORMAL, getattr(inst, field))
890
        self.assertEqual(row[fieldidx[field]], exp)
891

    
892
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
893

    
894
    # Ensure all possible status' have been tested
895
    self.assertEqual(tested_status, constants.INSTST_ALL)
896

    
897
  def _CheckInstanceConsole(self, instance, (status, consdata)):
898
    if instance.name == "inst7":
899
      self.assertEqual(status, constants.RS_NORMAL)
900
      console = objects.InstanceConsole.FromDict(consdata)
901
      self.assertTrue(console.Validate())
902
      self.assertEqual(console.host, instance.primary_node)
903
    else:
904
      self.assertEqual(status, constants.RS_UNAVAIL)
905

    
906

    
907
class TestGroupQuery(unittest.TestCase):
908

    
909
  def setUp(self):
910
    self.groups = [
911
      objects.NodeGroup(name="default",
912
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
913
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
914
      objects.NodeGroup(name="restricted",
915
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
916
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
917
      ]
918

    
919
  def _Create(self, selected):
920
    return query.Query(query.GROUP_FIELDS, selected)
921

    
922
  def testSimple(self):
923
    q = self._Create(["name", "uuid", "alloc_policy"])
924
    gqd = query.GroupQueryData(self.groups, None, None)
925

    
926
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
927

    
928
    self.assertEqual(q.Query(gqd),
929
      [[(constants.RS_NORMAL, "default"),
930
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
931
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
932
        ],
933
       [(constants.RS_NORMAL, "restricted"),
934
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
935
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
936
        ],
937
       ])
938

    
939
  def testNodes(self):
940
    groups_to_nodes = {
941
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
942
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
943
      }
944

    
945
    q = self._Create(["name", "node_cnt", "node_list"])
946
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
947

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

    
950
    self.assertEqual(q.Query(gqd),
951
                     [[(constants.RS_NORMAL, "default"),
952
                       (constants.RS_NORMAL, 2),
953
                       (constants.RS_NORMAL, ["node1", "node2"]),
954
                       ],
955
                      [(constants.RS_NORMAL, "restricted"),
956
                       (constants.RS_NORMAL, 3),
957
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
958
                       ],
959
                      ])
960

    
961
  def testInstances(self):
962
    groups_to_instances = {
963
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
964
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
965
      }
966

    
967
    q = self._Create(["pinst_cnt", "pinst_list"])
968
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
969

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

    
972
    self.assertEqual(q.Query(gqd),
973
                     [[(constants.RS_NORMAL, 2),
974
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
975
                       ],
976
                      [(constants.RS_NORMAL, 3),
977
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
978
                       ],
979
                      ])
980

    
981

    
982
class TestOsQuery(unittest.TestCase):
983
  def _Create(self, selected):
984
    return query.Query(query.OS_FIELDS, selected)
985

    
986
  def test(self):
987
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
988
    api_versions = [10, 0, 15, 5]
989
    parameters = ["zpar3", "apar9"]
990

    
991
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
992
    assert (api_versions != sorted(api_versions) and
993
            api_versions != utils.NiceSort(variants))
994
    assert (parameters != sorted(parameters) and
995
            parameters != utils.NiceSort(parameters))
996

    
997
    data = [
998
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
999
                   variants=set(), api_versions=set(), parameters=set(),
1000
                   node_status={ "some": "status", }),
1001
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1002
                   variants=set(variants),
1003
                   api_versions=set(api_versions),
1004
                   parameters=set(parameters),
1005
                   node_status={ "some": "other", "status": None, }),
1006
      ]
1007

    
1008

    
1009
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1010
                      "api_versions", "parameters", "node_status"])
1011
    self.assertEqual(q.RequestedData(), set([]))
1012
    self.assertEqual(q.Query(data),
1013
                     [[(constants.RS_NORMAL, "debian"),
1014
                       (constants.RS_NORMAL, False),
1015
                       (constants.RS_NORMAL, False),
1016
                       (constants.RS_NORMAL, False),
1017
                       (constants.RS_NORMAL, []),
1018
                       (constants.RS_NORMAL, []),
1019
                       (constants.RS_NORMAL, []),
1020
                       (constants.RS_NORMAL, {"some": "status"})],
1021
                      [(constants.RS_NORMAL, "dos"),
1022
                       (constants.RS_NORMAL, True),
1023
                       (constants.RS_NORMAL, False),
1024
                       (constants.RS_NORMAL, True),
1025
                       (constants.RS_NORMAL,
1026
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1027
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1028
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1029
                       (constants.RS_NORMAL,
1030
                        { "some": "other", "status": None, })
1031
                       ]])
1032

    
1033

    
1034
class TestQueryFields(unittest.TestCase):
1035
  def testAllFields(self):
1036
    for fielddefs in query.ALL_FIELD_LISTS:
1037
      result = query.QueryFields(fielddefs, None)
1038
      self.assert_(isinstance(result, dict))
1039
      response = objects.QueryFieldsResponse.FromDict(result)
1040
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1041
        [(fdef2.name, fdef2.title)
1042
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1043
                                                key=lambda x: x[0].name)])
1044

    
1045
  def testSomeFields(self):
1046
    rnd = random.Random(5357)
1047

    
1048
    for _ in range(10):
1049
      for fielddefs in query.ALL_FIELD_LISTS:
1050
        if len(fielddefs) > 20:
1051
          sample_size = rnd.randint(5, 20)
1052
        else:
1053
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1054
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1055
                                                         sample_size)]
1056
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1057
        self.assert_(isinstance(result, dict))
1058
        response = objects.QueryFieldsResponse.FromDict(result)
1059
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1060
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1061

    
1062

    
1063
class TestQueryFilter(unittest.TestCase):
1064
  def testRequestedNames(self):
1065
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1066

    
1067
    for fielddefs in query.ALL_FIELD_LISTS:
1068
      assert "name" in fielddefs
1069

    
1070
      # No name field
1071
      q = query.Query(fielddefs, ["name"], qfilter=["=", "name", "abc"],
1072
                      namefield=None)
1073
      self.assertEqual(q.RequestedNames(), None)
1074

    
1075
      # No filter
1076
      q = query.Query(fielddefs, ["name"], qfilter=None, namefield="name")
1077
      self.assertEqual(q.RequestedNames(), None)
1078

    
1079
      # Check empty query
1080
      q = query.Query(fielddefs, ["name"], qfilter=["|"], namefield="name")
1081
      self.assertEqual(q.RequestedNames(), None)
1082

    
1083
      # Check order
1084
      q = query.Query(fielddefs, ["name"], qfilter=["|"] + innerfilter,
1085
                      namefield="name")
1086
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1087

    
1088
      # Check reverse order
1089
      q = query.Query(fielddefs, ["name"],
1090
                      qfilter=["|"] + list(reversed(innerfilter)),
1091
                      namefield="name")
1092
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1093

    
1094
      # Duplicates
1095
      q = query.Query(fielddefs, ["name"],
1096
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1097
                      namefield="name")
1098
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1099

    
1100
      # Unknown name field
1101
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1102
                        namefield="_unknown_field_")
1103

    
1104
      # Filter with AND
1105
      q = query.Query(fielddefs, ["name"],
1106
                      qfilter=["|", ["=", "name", "foo"],
1107
                                    ["&", ["=", "name", ""]]],
1108
                      namefield="name")
1109
      self.assertTrue(q.RequestedNames() is None)
1110

    
1111
      # Filter with NOT
1112
      q = query.Query(fielddefs, ["name"],
1113
                      qfilter=["|", ["=", "name", "foo"],
1114
                                    ["!", ["=", "name", ""]]],
1115
                      namefield="name")
1116
      self.assertTrue(q.RequestedNames() is None)
1117

    
1118
      # Filter with only OR (names must be in correct order)
1119
      q = query.Query(fielddefs, ["name"],
1120
                      qfilter=["|", ["=", "name", "x17361"],
1121
                                    ["|", ["=", "name", "x22015"]],
1122
                                    ["|", ["|", ["=", "name", "x13193"]]],
1123
                                    ["=", "name", "x15215"]],
1124
                      namefield="name")
1125
      self.assertEqual(q.RequestedNames(),
1126
                       ["x17361", "x22015", "x13193", "x15215"])
1127

    
1128
  @staticmethod
1129
  def _GenNestedFilter(op, depth):
1130
    nested = ["=", "name", "value"]
1131
    for i in range(depth):
1132
      nested = [op, nested]
1133
    return nested
1134

    
1135
  def testCompileFilter(self):
1136
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1137

    
1138
    checks = [
1139
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1140
      ["=", "_unknown_field", "value"],
1141
      self._GenNestedFilter("|", levels_max),
1142
      self._GenNestedFilter("|", levels_max * 3),
1143
      self._GenNestedFilter("!", levels_max),
1144
      ]
1145

    
1146
    for fielddefs in query.ALL_FIELD_LISTS:
1147
      for qfilter in checks:
1148
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1149
                          fielddefs, None, qfilter)
1150

    
1151
      for op in ["|", "!"]:
1152
        qfilter = self._GenNestedFilter(op, levels_max - 1)
1153
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1154
                                                      qfilter)))
1155

    
1156
  def testQueryInputOrder(self):
1157
    fielddefs = query._PrepareFieldList([
1158
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1159
       None, 0, lambda ctx, item: item["pnode"]),
1160
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1161
       None, 0, lambda ctx, item: item["snode"]),
1162
      ], [])
1163

    
1164
    data = [
1165
      { "pnode": "node1", "snode": "node44", },
1166
      { "pnode": "node30", "snode": "node90", },
1167
      { "pnode": "node25", "snode": "node1", },
1168
      { "pnode": "node20", "snode": "node1", },
1169
      ]
1170

    
1171
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1172

    
1173
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1174
                    qfilter=qfilter)
1175
    self.assertTrue(q.RequestedNames() is None)
1176
    self.assertFalse(q.RequestedData())
1177
    self.assertEqual(q.Query(data),
1178
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1179
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1180
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1181

    
1182
    # Try again with reversed input data
1183
    self.assertEqual(q.Query(reversed(data)),
1184
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1185
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1186
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1187

    
1188
    # No name field, result must be in incoming order
1189
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1190
                    qfilter=qfilter)
1191
    self.assertFalse(q.RequestedData())
1192
    self.assertEqual(q.Query(data),
1193
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1194
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1195
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1196
    self.assertEqual(q.OldStyleQuery(data), [
1197
      ["node1", "node44"],
1198
      ["node25", "node1"],
1199
      ["node20", "node1"],
1200
      ])
1201
    self.assertEqual(q.Query(reversed(data)),
1202
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1203
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1204
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1205
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1206
      ["node20", "node1"],
1207
      ["node25", "node1"],
1208
      ["node1", "node44"],
1209
      ])
1210

    
1211
    # Name field, but no sorting, result must be in incoming order
1212
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1213
    self.assertFalse(q.RequestedData())
1214
    self.assertEqual(q.Query(data, sort_by_name=False),
1215
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1216
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1217
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1218
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1219
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1220
      ["node1", "node44"],
1221
      ["node30", "node90"],
1222
      ["node25", "node1"],
1223
      ["node20", "node1"],
1224
      ])
1225
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1226
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1227
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1228
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1229
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1230
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1231
      ["node20", "node1"],
1232
      ["node25", "node1"],
1233
      ["node30", "node90"],
1234
      ["node1", "node44"],
1235
      ])
1236

    
1237
  def testEqualNamesOrder(self):
1238
    fielddefs = query._PrepareFieldList([
1239
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1240
       None, 0, lambda ctx, item: item["pnode"]),
1241
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1242
       None, 0, lambda ctx, item: item["num"]),
1243
      ], [])
1244

    
1245
    data = [
1246
      { "pnode": "node1", "num": 100, },
1247
      { "pnode": "node1", "num": 25, },
1248
      { "pnode": "node2", "num": 90, },
1249
      { "pnode": "node2", "num": 30, },
1250
      ]
1251

    
1252
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1253
                    qfilter=["|", ["=", "pnode", "node1"],
1254
                                  ["=", "pnode", "node2"],
1255
                                  ["=", "pnode", "node1"]])
1256
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1257
                     msg="Did not return unique names")
1258
    self.assertFalse(q.RequestedData())
1259
    self.assertEqual(q.Query(data),
1260
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1261
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1262
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1263
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1264
    self.assertEqual(q.Query(data, sort_by_name=False),
1265
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1266
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1267
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1268
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1269

    
1270
    data = [
1271
      { "pnode": "nodeX", "num": 50, },
1272
      { "pnode": "nodeY", "num": 40, },
1273
      { "pnode": "nodeX", "num": 30, },
1274
      { "pnode": "nodeX", "num": 20, },
1275
      { "pnode": "nodeM", "num": 10, },
1276
      ]
1277

    
1278
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1279
                    qfilter=["|", ["=", "pnode", "nodeX"],
1280
                                  ["=", "pnode", "nodeY"],
1281
                                  ["=", "pnode", "nodeY"],
1282
                                  ["=", "pnode", "nodeY"],
1283
                                  ["=", "pnode", "nodeM"]])
1284
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1285
                     msg="Did not return unique names")
1286
    self.assertFalse(q.RequestedData())
1287

    
1288
    # First sorted by name, then input order
1289
    self.assertEqual(q.Query(data, sort_by_name=True),
1290
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1291
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1292
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1293
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1294
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1295

    
1296
    # Input order
1297
    self.assertEqual(q.Query(data, sort_by_name=False),
1298
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1299
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1300
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1301
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1302
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1303

    
1304
  def testFilter(self):
1305
    (DK_A, DK_B) = range(1000, 1002)
1306

    
1307
    fielddefs = query._PrepareFieldList([
1308
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1309
       DK_A, 0, lambda ctx, item: item["name"]),
1310
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1311
       DK_B, 0, lambda ctx, item: item["other"]),
1312
      ], [])
1313

    
1314
    data = [
1315
      { "name": "node1", "other": "foo", },
1316
      { "name": "node2", "other": "bar", },
1317
      { "name": "node3", "other": "Hello", },
1318
      ]
1319

    
1320
    # Empty filter
1321
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1322
                    qfilter=["|"])
1323
    self.assertTrue(q.RequestedNames() is None)
1324
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1325
    self.assertEqual(q.Query(data), [])
1326

    
1327
    # Normal filter
1328
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1329
                    qfilter=["=", "name", "node1"])
1330
    self.assertEqual(q.RequestedNames(), ["node1"])
1331
    self.assertEqual(q.Query(data),
1332
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1333

    
1334
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1335
                    qfilter=(["|", ["=", "name", "node1"],
1336
                                   ["=", "name", "node3"]]))
1337
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1338
    self.assertEqual(q.Query(data),
1339
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1340
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1341

    
1342
    # Complex filter
1343
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1344
                    qfilter=(["|", ["=", "name", "node1"],
1345
                                   ["|", ["=", "name", "node3"],
1346
                                         ["=", "name", "node2"]],
1347
                                   ["=", "name", "node3"]]))
1348
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1349
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1350
    self.assertEqual(q.Query(data),
1351
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1352
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1353
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1354

    
1355
    # Filter data type mismatch
1356
    for i in [-1, 0, 1, 123, [], None, True, False]:
1357
      self.assertRaises(errors.ParameterError, query.Query,
1358
                        fielddefs, ["name", "other"], namefield="name",
1359
                        qfilter=["=", "name", i])
1360

    
1361
    # Negative filter
1362
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1363
                    qfilter=["!", ["|", ["=", "name", "node1"],
1364
                                        ["=", "name", "node3"]]])
1365
    self.assertTrue(q.RequestedNames() is None)
1366
    self.assertEqual(q.Query(data),
1367
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1368

    
1369
    # Not equal
1370
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1371
                    qfilter=["!=", "name", "node3"])
1372
    self.assertTrue(q.RequestedNames() is None)
1373
    self.assertEqual(q.Query(data),
1374
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1375
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1376

    
1377
    # Data type
1378
    q = query.Query(fielddefs, [], namefield="name",
1379
                    qfilter=["|", ["=", "other", "bar"],
1380
                                  ["=", "name", "foo"]])
1381
    self.assertTrue(q.RequestedNames() is None)
1382
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1383
    self.assertEqual(q.Query(data), [[]])
1384

    
1385
    # Only one data type
1386
    q = query.Query(fielddefs, ["other"], namefield="name",
1387
                    qfilter=["=", "other", "bar"])
1388
    self.assertTrue(q.RequestedNames() is None)
1389
    self.assertEqual(q.RequestedData(), set([DK_B]))
1390
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1391

    
1392
    q = query.Query(fielddefs, [], namefield="name",
1393
                    qfilter=["=", "other", "bar"])
1394
    self.assertTrue(q.RequestedNames() is None)
1395
    self.assertEqual(q.RequestedData(), set([DK_B]))
1396
    self.assertEqual(q.Query(data), [[]])
1397

    
1398
  def testFilterContains(self):
1399
    fielddefs = query._PrepareFieldList([
1400
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1401
       None, 0, lambda ctx, item: item["name"]),
1402
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1403
       None, 0, lambda ctx, item: item["other"]),
1404
      ], [])
1405

    
1406
    data = [
1407
      { "name": "node2", "other": ["x", "y", "bar"], },
1408
      { "name": "node3", "other": "Hello", },
1409
      { "name": "node1", "other": ["a", "b", "foo"], },
1410
      { "name": "empty", "other": []},
1411
      ]
1412

    
1413
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1414
                    qfilter=["=[]", "other", "bar"])
1415
    self.assertTrue(q.RequestedNames() is None)
1416
    self.assertEqual(q.Query(data), [
1417
      [(constants.RS_NORMAL, "node2"),
1418
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1419
      ])
1420

    
1421
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1422
                    qfilter=["|", ["=[]", "other", "bar"],
1423
                                  ["=[]", "other", "a"],
1424
                                  ["=[]", "other", "b"]])
1425
    self.assertTrue(q.RequestedNames() is None)
1426
    self.assertEqual(q.Query(data), [
1427
      [(constants.RS_NORMAL, "node1"),
1428
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1429
      [(constants.RS_NORMAL, "node2"),
1430
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1431
      ])
1432
    self.assertEqual(q.OldStyleQuery(data), [
1433
      ["node1", ["a", "b", "foo"]],
1434
      ["node2", ["x", "y", "bar"]],
1435
      ])
1436

    
1437
    # Boolean test
1438
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1439
                    qfilter=["?", "other"])
1440
    self.assertEqual(q.OldStyleQuery(data), [
1441
      ["node1", ["a", "b", "foo"]],
1442
      ["node2", ["x", "y", "bar"]],
1443
      ["node3", "Hello"],
1444
      ])
1445

    
1446
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1447
                    qfilter=["!", ["?", "other"]])
1448
    self.assertEqual(q.OldStyleQuery(data), [
1449
      ["empty", []],
1450
      ])
1451

    
1452
  def testFilterHostname(self):
1453
    fielddefs = query._PrepareFieldList([
1454
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1455
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1456
      ], [])
1457

    
1458
    data = [
1459
      { "name": "node1.example.com", },
1460
      { "name": "node2.example.com", },
1461
      { "name": "node2.example.net", },
1462
      ]
1463

    
1464
    q = query.Query(fielddefs, ["name"], namefield="name",
1465
                    qfilter=["=", "name", "node2"])
1466
    self.assertEqual(q.RequestedNames(), ["node2"])
1467
    self.assertEqual(q.Query(data), [
1468
      [(constants.RS_NORMAL, "node2.example.com")],
1469
      [(constants.RS_NORMAL, "node2.example.net")],
1470
      ])
1471

    
1472
    q = query.Query(fielddefs, ["name"], namefield="name",
1473
                    qfilter=["=", "name", "node1"])
1474
    self.assertEqual(q.RequestedNames(), ["node1"])
1475
    self.assertEqual(q.Query(data), [
1476
      [(constants.RS_NORMAL, "node1.example.com")],
1477
      ])
1478

    
1479
    q = query.Query(fielddefs, ["name"], namefield="name",
1480
                    qfilter=["=", "name", "othername"])
1481
    self.assertEqual(q.RequestedNames(), ["othername"])
1482
    self.assertEqual(q.Query(data), [])
1483

    
1484
    q = query.Query(fielddefs, ["name"], namefield="name",
1485
                    qfilter=["|", ["=", "name", "node1.example.com"],
1486
                                  ["=", "name", "node2"]])
1487
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1488
    self.assertEqual(q.Query(data), [
1489
      [(constants.RS_NORMAL, "node1.example.com")],
1490
      [(constants.RS_NORMAL, "node2.example.com")],
1491
      [(constants.RS_NORMAL, "node2.example.net")],
1492
      ])
1493
    self.assertEqual(q.OldStyleQuery(data), [
1494
      ["node1.example.com"],
1495
      ["node2.example.com"],
1496
      ["node2.example.net"],
1497
      ])
1498

    
1499
    q = query.Query(fielddefs, ["name"], namefield="name",
1500
                    qfilter=["!=", "name", "node1"])
1501
    self.assertTrue(q.RequestedNames() is None)
1502
    self.assertEqual(q.Query(data), [
1503
      [(constants.RS_NORMAL, "node2.example.com")],
1504
      [(constants.RS_NORMAL, "node2.example.net")],
1505
      ])
1506
    self.assertEqual(q.OldStyleQuery(data), [
1507
      ["node2.example.com"],
1508
      ["node2.example.net"],
1509
      ])
1510

    
1511
  def testFilterBoolean(self):
1512
    fielddefs = query._PrepareFieldList([
1513
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1514
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1515
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1516
       None, 0, lambda ctx, item: item["value"]),
1517
      ], [])
1518

    
1519
    data = [
1520
      { "name": "node1", "value": False, },
1521
      { "name": "node2", "value": True, },
1522
      { "name": "node3", "value": True, },
1523
      ]
1524

    
1525
    q = query.Query(fielddefs, ["name", "value"],
1526
                    qfilter=["|", ["=", "value", False],
1527
                                  ["=", "value", True]])
1528
    self.assertTrue(q.RequestedNames() is None)
1529
    self.assertEqual(q.Query(data), [
1530
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1531
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1532
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1533
      ])
1534

    
1535
    q = query.Query(fielddefs, ["name", "value"],
1536
                    qfilter=["|", ["=", "value", False],
1537
                                  ["!", ["=", "value", False]]])
1538
    self.assertTrue(q.RequestedNames() is None)
1539
    self.assertEqual(q.Query(data), [
1540
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1541
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1542
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1543
      ])
1544

    
1545
    # Comparing bool with string
1546
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1547
      self.assertRaises(errors.ParameterError, query.Query,
1548
                        fielddefs, ["name", "value"],
1549
                        qfilter=["=", "value", i])
1550

    
1551
    # Truth filter
1552
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1553
    self.assertTrue(q.RequestedNames() is None)
1554
    self.assertEqual(q.Query(data), [
1555
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1556
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1557
      ])
1558

    
1559
    # Negative bool filter
1560
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1561
    self.assertTrue(q.RequestedNames() is None)
1562
    self.assertEqual(q.Query(data), [
1563
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1564
      ])
1565

    
1566
    # Complex truth filter
1567
    q = query.Query(fielddefs, ["name", "value"],
1568
                    qfilter=["|", ["&", ["=", "name", "node1"],
1569
                                        ["!", ["?", "value"]]],
1570
                                  ["?", "value"]])
1571
    self.assertTrue(q.RequestedNames() is None)
1572
    self.assertEqual(q.Query(data), [
1573
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1574
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1575
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1576
      ])
1577

    
1578
  def testFilterRegex(self):
1579
    fielddefs = query._PrepareFieldList([
1580
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1581
       None, 0, lambda ctx, item: item["name"]),
1582
      ], [])
1583

    
1584
    data = [
1585
      { "name": "node1.example.com", },
1586
      { "name": "node2.site.example.com", },
1587
      { "name": "node2.example.net", },
1588

    
1589
      # Empty name
1590
      { "name": "", },
1591
      ]
1592

    
1593
    q = query.Query(fielddefs, ["name"], namefield="name",
1594
                    qfilter=["=~", "name", "site"])
1595
    self.assertTrue(q.RequestedNames() is None)
1596
    self.assertEqual(q.Query(data), [
1597
      [(constants.RS_NORMAL, "node2.site.example.com")],
1598
      ])
1599

    
1600
    q = query.Query(fielddefs, ["name"], namefield="name",
1601
                    qfilter=["=~", "name", "^node2"])
1602
    self.assertTrue(q.RequestedNames() is None)
1603
    self.assertEqual(q.Query(data), [
1604
      [(constants.RS_NORMAL, "node2.example.net")],
1605
      [(constants.RS_NORMAL, "node2.site.example.com")],
1606
      ])
1607

    
1608
    q = query.Query(fielddefs, ["name"], namefield="name",
1609
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1610
    self.assertTrue(q.RequestedNames() is None)
1611
    self.assertEqual(q.Query(data), [
1612
      [(constants.RS_NORMAL, "node1.example.com")],
1613
      [(constants.RS_NORMAL, "node2.site.example.com")],
1614
      ])
1615

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

    
1625
    q = query.Query(fielddefs, ["name"], namefield="name",
1626
                    qfilter=["=~", "name", r"^$"])
1627
    self.assertTrue(q.RequestedNames() is None)
1628
    self.assertEqual(q.Query(data), [
1629
      [(constants.RS_NORMAL, "")],
1630
      ])
1631

    
1632
    # Invalid regular expression
1633
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1634
                      qfilter=["=~", "name", r"["])
1635

    
1636

    
1637
if __name__ == "__main__":
1638
  testutils.GanetiTestProgram()