Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ fab9573b

History | View | Annotate | Download (63.2 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
  def testUnknown(self):
226
    fielddef = query._PrepareFieldList([
227
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
228
       None, 0, lambda _, item: "name%s" % item),
229
      (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP, "Other"),
230
       None, 0, lambda *args: 1234),
231
      (query._MakeField("nodata", "NoData", constants.QFT_NUMBER, "No data"),
232
       None, 0, lambda *args: query._FS_NODATA ),
233
      (query._MakeField("unavail", "Unavail", constants.QFT_BOOL, "Unavail"),
234
       None, 0, lambda *args: query._FS_UNAVAIL),
235
      ], [])
236

    
237
    for selected in [["foo"], ["Hello", "World"],
238
                     ["name1", "other", "foo"]]:
239
      q = query.Query(fielddef, selected)
240
      self.assertEqual(len(q._fields), len(selected))
241
      self.assert_(compat.all(len(row) == len(selected)
242
                              for row in q.Query(_QueryData(range(1, 10)))))
243
      self.assertEqual(q.Query(_QueryData(range(1, 10))),
244
                       [[(constants.RS_UNKNOWN, None)] * len(selected)
245
                        for i in range(1, 10)])
246
      self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
247
                       [{ "name": name, "title": name,
248
                          "kind": constants.QFT_UNKNOWN,
249
                          "doc": "Unknown field '%s'" % name}
250
                        for name in selected])
251

    
252
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
253
    self.assertEqual(len(q._fields), 4)
254
    self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
255
                     ["name%s" % i, 1234, None, None]
256
                     for i in range(1, 10)
257
                     ])
258

    
259
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
260
    self.assertEqual(len(q._fields), 5)
261
    self.assertEqual(q.Query(_QueryData(range(1, 10))),
262
                     [[(constants.RS_NORMAL, "name%s" % i),
263
                       (constants.RS_NORMAL, 1234),
264
                       (constants.RS_NODATA, None),
265
                       (constants.RS_UNAVAIL, None),
266
                       (constants.RS_UNKNOWN, None)]
267
                      for i in range(1, 10)])
268

    
269
  def testAliases(self):
270
    fields = [
271
      (query._MakeField("a", "a-title", constants.QFT_TEXT, "Field A"),
272
       None, 0, lambda *args: None),
273
      (query._MakeField("b", "b-title", constants.QFT_TEXT, "Field B"),
274
       None, 0, lambda *args: None),
275
      ]
276
    # duplicate field
277
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
278
                      [("b", "a")])
279
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
280
                      [("c", "b"), ("c", "a")])
281
    # missing target
282
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
283
                      [("c", "d")])
284
    fdefs = query._PrepareFieldList(fields, [("c", "b")])
285
    self.assertEqual(len(fdefs), 3)
286
    self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
287

    
288

    
289
class TestGetNodeRole(unittest.TestCase):
290
  def test(self):
291
    tested_role = set()
292

    
293
    checks = [
294
      (constants.NR_MASTER, "node1", objects.Node(name="node1")),
295
      (constants.NR_MCANDIDATE, "master",
296
       objects.Node(name="node1", master_candidate=True)),
297
      (constants.NR_REGULAR, "master", objects.Node(name="node1")),
298
      (constants.NR_DRAINED, "master",
299
       objects.Node(name="node1", drained=True)),
300
      (constants.NR_OFFLINE,
301
       "master", objects.Node(name="node1", offline=True)),
302
      ]
303

    
304
    for (role, master_name, node) in checks:
305
      result = query._GetNodeRole(node, master_name)
306
      self.assertEqual(result, role)
307
      tested_role.add(result)
308

    
309
    self.assertEqual(tested_role, constants.NR_ALL)
310

    
311

    
312
class TestNodeQuery(unittest.TestCase):
313
  def _Create(self, selected):
314
    return query.Query(query.NODE_FIELDS, selected)
315

    
316
  def testSimple(self):
317
    nodes = [
318
      objects.Node(name="node1", drained=False),
319
      objects.Node(name="node2", drained=True),
320
      objects.Node(name="node3", drained=False),
321
      ]
322
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
323
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None,
324
                                None)
325

    
326
      q = self._Create(["name", "drained"])
327
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
328
      self.assertEqual(q.Query(nqd),
329
                       [[(constants.RS_NORMAL, "node1"),
330
                         (constants.RS_NORMAL, False)],
331
                        [(constants.RS_NORMAL, "node2"),
332
                         (constants.RS_NORMAL, True)],
333
                        [(constants.RS_NORMAL, "node3"),
334
                         (constants.RS_NORMAL, False)],
335
                       ])
336
      self.assertEqual(q.OldStyleQuery(nqd),
337
                       [["node1", False],
338
                        ["node2", True],
339
                        ["node3", False]])
340

    
341
  def test(self):
342
    selected = query.NODE_FIELDS.keys()
343
    field_index = dict((field, idx) for idx, field in enumerate(selected))
344

    
345
    q = self._Create(selected)
346
    self.assertEqual(q.RequestedData(),
347
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
348
                          query.NQ_GROUP, query.NQ_OOB]))
349

    
350
    cluster = objects.Cluster(cluster_name="testcluster",
351
      hvparams=constants.HVC_DEFAULTS,
352
      beparams={
353
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
354
        },
355
      nicparams={
356
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
357
        },
358
      ndparams=constants.NDC_DEFAULTS,
359
        )
360

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

    
382
    master_node = nodes[3]
383
    master_node.AddTag("masternode")
384
    master_node.AddTag("another")
385
    master_node.AddTag("tag")
386
    master_node.ctime = None
387
    master_node.mtime = None
388
    assert master_node.name == master_name
389

    
390
    live_data_name = node_names[4]
391
    assert live_data_name != master_name
392

    
393
    fake_live_data = {
394
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
395
      "cnodes": 4,
396
      "csockets": 4,
397
      "ctotal": 8,
398
      "mnode": 128,
399
      "mfree": 100,
400
      "mtotal": 4096,
401
      "dfree": 5 * 1024 * 1024,
402
      "dtotal": 100 * 1024 * 1024,
403
      }
404

    
405
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
406
            sorted(fake_live_data.keys()))
407

    
408
    live_data = dict.fromkeys(node_names, {})
409
    live_data[live_data_name] = \
410
      dict((query._NODE_LIVE_FIELDS[name][2], value)
411
           for name, value in fake_live_data.items())
412

    
413
    node_to_primary = dict((name, set()) for name in node_names)
414
    node_to_primary[master_name].update(["inst1", "inst2"])
415

    
416
    node_to_secondary = dict((name, set()) for name in node_names)
417
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
418

    
419
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
420
    groups = {
421
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
422
      }
423

    
424
    oob_not_powered_node = node_names[0]
425
    nodes[0].powered = False
426
    oob_support = dict((name, False) for name in node_names)
427
    oob_support[master_name] = True
428
    oob_support[oob_not_powered_node] = True
429

    
430
    master_node.group = ng_uuid
431

    
432
    nqd = query.NodeQueryData(nodes, live_data, master_name,
433
                              node_to_primary, node_to_secondary, groups,
434
                              oob_support, cluster)
435
    result = q.Query(nqd)
436
    self.assert_(compat.all(len(row) == len(selected) for row in result))
437
    self.assertEqual([row[field_index["name"]] for row in result],
438
                     [(constants.RS_NORMAL, name) for name in node_names])
439

    
440
    node_to_row = dict((row[field_index["name"]][1], idx)
441
                       for idx, row in enumerate(result))
442

    
443
    master_row = result[node_to_row[master_name]]
444
    self.assert_(master_row[field_index["master"]])
445
    self.assert_(master_row[field_index["role"]], "M")
446
    self.assertEqual(master_row[field_index["group"]],
447
                     (constants.RS_NORMAL, "ng1"))
448
    self.assertEqual(master_row[field_index["group.uuid"]],
449
                     (constants.RS_NORMAL, ng_uuid))
450
    self.assertEqual(master_row[field_index["ctime"]],
451
                     (constants.RS_UNAVAIL, None))
452
    self.assertEqual(master_row[field_index["mtime"]],
453
                     (constants.RS_UNAVAIL, None))
454

    
455
    self.assert_(row[field_index["pip"]] == node.primary_ip and
456
                 row[field_index["sip"]] == node.secondary_ip and
457
                 set(row[field_index["tags"]]) == node.GetTags() and
458
                 row[field_index["serial_no"]] == node.serial_no and
459
                 row[field_index["role"]] == query._GetNodeRole(node,
460
                                                                master_name) and
461
                 (node.name == master_name or
462
                  (row[field_index["group"]] == "<unknown>" and
463
                   row[field_index["group.uuid"]] is None and
464
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
465
                                                 node.ctime) and
466
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
467
                                                 node.mtime) and
468
                   row[field_index["powered"]] == (constants.RS_NORMAL,
469
                                                   True))) or
470
                 (node.name == oob_not_powered_node and
471
                  row[field_index["powered"]] == (constants.RS_NORMAL,
472
                                                  False)) or
473
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
474
                 for row, node in zip(result, nodes))
475

    
476
    live_data_row = result[node_to_row[live_data_name]]
477

    
478
    for (field, value) in fake_live_data.items():
479
      self.assertEqual(live_data_row[field_index[field]],
480
                       (constants.RS_NORMAL, value))
481

    
482
    self.assertEqual(master_row[field_index["pinst_cnt"]],
483
                     (constants.RS_NORMAL, 2))
484
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
485
                     (constants.RS_NORMAL, 3))
486
    self.assertEqual(master_row[field_index["pinst_list"]],
487
                     (constants.RS_NORMAL,
488
                      list(node_to_primary[master_name])))
489
    self.assertEqual(live_data_row[field_index["sinst_list"]],
490
                     (constants.RS_NORMAL,
491
                      list(node_to_secondary[live_data_name])))
492

    
493
  def testGetLiveNodeField(self):
494
    nodes = [
495
      objects.Node(name="node1", drained=False, offline=False,
496
                   vm_capable=True),
497
      objects.Node(name="node2", drained=True, offline=False,
498
                   vm_capable=True),
499
      objects.Node(name="node3", drained=False, offline=False,
500
                   vm_capable=True),
501
      objects.Node(name="node4", drained=False, offline=True,
502
                   vm_capable=True),
503
      objects.Node(name="node5", drained=False, offline=False,
504
                   vm_capable=False),
505
      ]
506
    live_data = dict.fromkeys([node.name for node in nodes], {})
507

    
508
    # No data
509
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
510
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
511
                                             nqd, nodes[0]),
512
                     query._FS_NODATA)
513

    
514
    # Missing field
515
    ctx = _QueryData(None, curlive_data={
516
      "some": 1,
517
      "other": 2,
518
      })
519
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
520
                                             ctx, nodes[0]),
521
                     query._FS_UNAVAIL)
522

    
523
    # Wrong format/datatype
524
    ctx = _QueryData(None, curlive_data={
525
      "hello": ["Hello World"],
526
      "other": 2,
527
      })
528
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
529
                                             ctx, nodes[0]),
530
                     query._FS_UNAVAIL)
531

    
532
    # Offline node
533
    assert nodes[3].offline
534
    ctx = _QueryData(None, curlive_data={})
535
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
536
                                             ctx, nodes[3]),
537
                     query._FS_OFFLINE, None)
538

    
539
    # Wrong field type
540
    ctx = _QueryData(None, curlive_data={"hello": 123})
541
    self.assertRaises(AssertionError, query._GetLiveNodeField,
542
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
543

    
544
    # Non-vm_capable node
545
    assert not nodes[4].vm_capable
546
    ctx = _QueryData(None, curlive_data={})
547
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
548
                                             ctx, nodes[4]),
549
                     query._FS_UNAVAIL, None)
550

    
551

    
552
class TestInstanceQuery(unittest.TestCase):
553
  def _Create(self, selected):
554
    return query.Query(query.INSTANCE_FIELDS, selected)
555

    
556
  def testSimple(self):
557
    q = self._Create(["name", "be/memory", "ip"])
558
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
559

    
560
    cluster = objects.Cluster(cluster_name="testcluster",
561
      hvparams=constants.HVC_DEFAULTS,
562
      beparams={
563
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
564
        },
565
      nicparams={
566
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
567
        },
568
      os_hvp={},
569
      osparams={})
570

    
571
    instances = [
572
      objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
573
                       nics=[], os="deb1"),
574
      objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
575
        os="foomoo",
576
        beparams={
577
          constants.BE_MEMORY: 512,
578
        }),
579
      objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
580
        os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
581
      ]
582

    
583
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
584
                                  set(), {}, None, None)
585
    self.assertEqual(q.Query(iqd),
586
      [[(constants.RS_NORMAL, "inst1"),
587
        (constants.RS_NORMAL, 128),
588
        (constants.RS_UNAVAIL, None),
589
       ],
590
       [(constants.RS_NORMAL, "inst2"),
591
        (constants.RS_NORMAL, 512),
592
        (constants.RS_UNAVAIL, None),
593
       ],
594
       [(constants.RS_NORMAL, "inst3"),
595
        (constants.RS_NORMAL, 128),
596
        (constants.RS_NORMAL, "192.0.2.99"),
597
       ]])
598
    self.assertEqual(q.OldStyleQuery(iqd),
599
      [["inst1", 128, None],
600
       ["inst2", 512, None],
601
       ["inst3", 128, "192.0.2.99"]])
602

    
603
  def test(self):
604
    selected = query.INSTANCE_FIELDS.keys()
605
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
606

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

    
610
    q = self._Create(selected)
611
    self.assertEqual(q.RequestedData(),
612
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
613
                          query.IQ_CONSOLE, query.IQ_NODES]))
614

    
615
    cluster = objects.Cluster(cluster_name="testcluster",
616
      hvparams=constants.HVC_DEFAULTS,
617
      beparams={
618
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
619
        },
620
      nicparams={
621
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
622
        },
623
      os_hvp={},
624
      tcpudp_port_pool=set(),
625
      osparams={
626
        "deb99": {
627
          "clean_install": "yes",
628
          },
629
        })
630

    
631
    offline_nodes = ["nodeoff1", "nodeoff2"]
632
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
633
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
634

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

    
729
    assert not utils.FindDuplicates(inst.name for inst in instances)
730

    
731
    instbyname = dict((inst.name, inst) for inst in instances)
732

    
733
    disk_usage = dict((inst.name,
734
                       cmdlib._ComputeDiskSize(inst.disk_template,
735
                                               [{"size": disk.size}
736
                                                for disk in inst.disks]))
737
                      for inst in instances)
738

    
739
    inst_bridges = {
740
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
741
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
742
                None, "eth123"],
743
      }
744

    
745
    live_data = {
746
      "inst2": {
747
        "vcpus": 3,
748
        },
749
      "inst4": {
750
        "memory": 123,
751
        },
752
      "inst6": {
753
        "memory": 768,
754
        },
755
      "inst7": {
756
        "vcpus": 3,
757
        },
758
      }
759
    wrongnode_inst = set(["inst7"])
760

    
761
    consinfo = dict((inst.name, None) for inst in instances)
762
    consinfo["inst7"] = \
763
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
764
                              host=instbyname["inst7"].primary_node,
765
                              user=constants.GANETI_RUNAS,
766
                              command=["hostname"]).ToDict()
767

    
768
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
769
                                  offline_nodes, bad_nodes, live_data,
770
                                  wrongnode_inst, consinfo, {}, {})
771
    result = q.Query(iqd)
772
    self.assertEqual(len(result), len(instances))
773
    self.assert_(compat.all(len(row) == len(selected)
774
                            for row in result))
775

    
776
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
777
           "Offline nodes not included in bad nodes"
778

    
779
    tested_status = set()
780

    
781
    for (inst, row) in zip(instances, result):
782
      assert inst.primary_node in nodes
783

    
784
      self.assertEqual(row[fieldidx["name"]],
785
                       (constants.RS_NORMAL, inst.name))
786

    
787
      if inst.primary_node in offline_nodes:
788
        exp_status = constants.INSTST_NODEOFFLINE
789
      elif inst.primary_node in bad_nodes:
790
        exp_status = constants.INSTST_NODEDOWN
791
      elif inst.name in live_data:
792
        if inst.name in wrongnode_inst:
793
          exp_status = constants.INSTST_WRONGNODE
794
        elif inst.admin_up:
795
          exp_status = constants.INSTST_RUNNING
796
        else:
797
          exp_status = constants.INSTST_ERRORUP
798
      elif inst.admin_up:
799
        exp_status = constants.INSTST_ERRORDOWN
800
      else:
801
        exp_status = constants.INSTST_ADMINDOWN
802

    
803
      self.assertEqual(row[fieldidx["status"]],
804
                       (constants.RS_NORMAL, exp_status))
805

    
806
      (_, status) = row[fieldidx["status"]]
807
      tested_status.add(status)
808

    
809
      for (field, livefield) in [("oper_ram", "memory"),
810
                                 ("oper_vcpus", "vcpus")]:
811
        if inst.primary_node in bad_nodes:
812
          exp = (constants.RS_NODATA, None)
813
        elif inst.name in live_data:
814
          value = live_data[inst.name].get(livefield, None)
815
          if value is None:
816
            exp = (constants.RS_UNAVAIL, None)
817
          else:
818
            exp = (constants.RS_NORMAL, value)
819
        else:
820
          exp = (constants.RS_UNAVAIL, None)
821

    
822
        self.assertEqual(row[fieldidx[field]], exp)
823

    
824
      bridges = inst_bridges.get(inst.name, [])
825
      self.assertEqual(row[fieldidx["nic.bridges"]],
826
                       (constants.RS_NORMAL, bridges))
827
      if bridges:
828
        self.assertEqual(row[fieldidx["bridge"]],
829
                         (constants.RS_NORMAL, bridges[0]))
830
      else:
831
        self.assertEqual(row[fieldidx["bridge"]],
832
                         (constants.RS_UNAVAIL, None))
833

    
834
      for i in range(constants.MAX_NICS):
835
        if i < len(bridges) and bridges[i] is not None:
836
          exp = (constants.RS_NORMAL, bridges[i])
837
        else:
838
          exp = (constants.RS_UNAVAIL, None)
839
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
840

    
841
      if inst.primary_node in bad_nodes:
842
        exp = (constants.RS_NODATA, None)
843
      else:
844
        exp = (constants.RS_NORMAL, inst.name in live_data)
845
      self.assertEqual(row[fieldidx["oper_state"]], exp)
846

    
847
      cust_exp = (constants.RS_NORMAL, {})
848
      if inst.os == "deb99":
849
        if inst.name == "inst6":
850
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
851
          cust_exp = exp
852
        else:
853
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
854
      else:
855
        exp = (constants.RS_NORMAL, {})
856
      self.assertEqual(row[fieldidx["osparams"]], exp)
857
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
858

    
859
      usage = disk_usage[inst.name]
860
      if usage is None:
861
        usage = 0
862
      self.assertEqual(row[fieldidx["disk_usage"]],
863
                       (constants.RS_NORMAL, usage))
864

    
865
      for alias, target in [("sda_size", "disk.size/0"),
866
                            ("sdb_size", "disk.size/1"),
867
                            ("vcpus", "be/vcpus"),
868
                            ("ip", "nic.ip/0"),
869
                            ("mac", "nic.mac/0"),
870
                            ("bridge", "nic.bridge/0"),
871
                            ("nic_mode", "nic.mode/0"),
872
                            ("nic_link", "nic.link/0"),
873
                            ]:
874
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
875

    
876
      for field in ["ctime", "mtime"]:
877
        if getattr(inst, field) is None:
878
          # No ctime/mtime
879
          exp = (constants.RS_UNAVAIL, None)
880
        else:
881
          exp = (constants.RS_NORMAL, getattr(inst, field))
882
        self.assertEqual(row[fieldidx[field]], exp)
883

    
884
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
885

    
886
    # Ensure all possible status' have been tested
887
    self.assertEqual(tested_status, constants.INSTST_ALL)
888

    
889
  def _CheckInstanceConsole(self, instance, (status, consdata)):
890
    if instance.name == "inst7":
891
      self.assertEqual(status, constants.RS_NORMAL)
892
      console = objects.InstanceConsole.FromDict(consdata)
893
      self.assertTrue(console.Validate())
894
      self.assertEqual(console.host, instance.primary_node)
895
    else:
896
      self.assertEqual(status, constants.RS_UNAVAIL)
897

    
898

    
899
class TestGroupQuery(unittest.TestCase):
900

    
901
  def setUp(self):
902
    self.groups = [
903
      objects.NodeGroup(name="default",
904
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
905
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
906
      objects.NodeGroup(name="restricted",
907
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
908
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
909
      ]
910

    
911
  def _Create(self, selected):
912
    return query.Query(query.GROUP_FIELDS, selected)
913

    
914
  def testSimple(self):
915
    q = self._Create(["name", "uuid", "alloc_policy"])
916
    gqd = query.GroupQueryData(self.groups, None, None)
917

    
918
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
919

    
920
    self.assertEqual(q.Query(gqd),
921
      [[(constants.RS_NORMAL, "default"),
922
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
923
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
924
        ],
925
       [(constants.RS_NORMAL, "restricted"),
926
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
927
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
928
        ],
929
       ])
930

    
931
  def testNodes(self):
932
    groups_to_nodes = {
933
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
934
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
935
      }
936

    
937
    q = self._Create(["name", "node_cnt", "node_list"])
938
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
939

    
940
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
941

    
942
    self.assertEqual(q.Query(gqd),
943
                     [[(constants.RS_NORMAL, "default"),
944
                       (constants.RS_NORMAL, 2),
945
                       (constants.RS_NORMAL, ["node1", "node2"]),
946
                       ],
947
                      [(constants.RS_NORMAL, "restricted"),
948
                       (constants.RS_NORMAL, 3),
949
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
950
                       ],
951
                      ])
952

    
953
  def testInstances(self):
954
    groups_to_instances = {
955
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
956
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
957
      }
958

    
959
    q = self._Create(["pinst_cnt", "pinst_list"])
960
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
961

    
962
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
963

    
964
    self.assertEqual(q.Query(gqd),
965
                     [[(constants.RS_NORMAL, 2),
966
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
967
                       ],
968
                      [(constants.RS_NORMAL, 3),
969
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
970
                       ],
971
                      ])
972

    
973

    
974
class TestOsQuery(unittest.TestCase):
975
  def _Create(self, selected):
976
    return query.Query(query.OS_FIELDS, selected)
977

    
978
  def test(self):
979
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
980
    api_versions = [10, 0, 15, 5]
981
    parameters = ["zpar3", "apar9"]
982

    
983
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
984
    assert (api_versions != sorted(api_versions) and
985
            api_versions != utils.NiceSort(variants))
986
    assert (parameters != sorted(parameters) and
987
            parameters != utils.NiceSort(parameters))
988

    
989
    data = [
990
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
991
                   variants=set(), api_versions=set(), parameters=set(),
992
                   node_status={ "some": "status", }),
993
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
994
                   variants=set(variants),
995
                   api_versions=set(api_versions),
996
                   parameters=set(parameters),
997
                   node_status={ "some": "other", "status": None, }),
998
      ]
999

    
1000

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

    
1025

    
1026
class TestQueryFields(unittest.TestCase):
1027
  def testAllFields(self):
1028
    for fielddefs in query.ALL_FIELD_LISTS:
1029
      result = query.QueryFields(fielddefs, None)
1030
      self.assert_(isinstance(result, dict))
1031
      response = objects.QueryFieldsResponse.FromDict(result)
1032
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1033
        [(fdef2.name, fdef2.title)
1034
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1035
                                                key=lambda x: x[0].name)])
1036

    
1037
  def testSomeFields(self):
1038
    rnd = random.Random(5357)
1039

    
1040
    for _ in range(10):
1041
      for fielddefs in query.ALL_FIELD_LISTS:
1042
        if len(fielddefs) > 20:
1043
          sample_size = rnd.randint(5, 20)
1044
        else:
1045
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1046
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1047
                                                         sample_size)]
1048
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1049
        self.assert_(isinstance(result, dict))
1050
        response = objects.QueryFieldsResponse.FromDict(result)
1051
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1052
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1053

    
1054

    
1055
class TestQueryFilter(unittest.TestCase):
1056
  def testRequestedNames(self):
1057
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1058

    
1059
    for fielddefs in query.ALL_FIELD_LISTS:
1060
      assert "name" in fielddefs
1061

    
1062
      # No name field
1063
      q = query.Query(fielddefs, ["name"], filter_=["=", "name", "abc"],
1064
                      namefield=None)
1065
      self.assertEqual(q.RequestedNames(), None)
1066

    
1067
      # No filter
1068
      q = query.Query(fielddefs, ["name"], filter_=None, namefield="name")
1069
      self.assertEqual(q.RequestedNames(), None)
1070

    
1071
      # Check empty query
1072
      q = query.Query(fielddefs, ["name"], filter_=["|"], namefield="name")
1073
      self.assertEqual(q.RequestedNames(), None)
1074

    
1075
      # Check order
1076
      q = query.Query(fielddefs, ["name"], filter_=["|"] + innerfilter,
1077
                      namefield="name")
1078
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1079

    
1080
      # Check reverse order
1081
      q = query.Query(fielddefs, ["name"],
1082
                      filter_=["|"] + list(reversed(innerfilter)),
1083
                      namefield="name")
1084
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1085

    
1086
      # Duplicates
1087
      q = query.Query(fielddefs, ["name"],
1088
                      filter_=["|"] + innerfilter + list(reversed(innerfilter)),
1089
                      namefield="name")
1090
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1091

    
1092
      # Unknown name field
1093
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1094
                        namefield="_unknown_field_")
1095

    
1096
      # Filter with AND
1097
      q = query.Query(fielddefs, ["name"],
1098
                      filter_=["|", ["=", "name", "foo"],
1099
                                    ["&", ["=", "name", ""]]],
1100
                      namefield="name")
1101
      self.assertTrue(q.RequestedNames() is None)
1102

    
1103
      # Filter with NOT
1104
      q = query.Query(fielddefs, ["name"],
1105
                      filter_=["|", ["=", "name", "foo"],
1106
                                    ["!", ["=", "name", ""]]],
1107
                      namefield="name")
1108
      self.assertTrue(q.RequestedNames() is None)
1109

    
1110
      # Filter with only OR (names must be in correct order)
1111
      q = query.Query(fielddefs, ["name"],
1112
                      filter_=["|", ["=", "name", "x17361"],
1113
                                    ["|", ["=", "name", "x22015"]],
1114
                                    ["|", ["|", ["=", "name", "x13193"]]],
1115
                                    ["=", "name", "x15215"]],
1116
                      namefield="name")
1117
      self.assertEqual(q.RequestedNames(),
1118
                       ["x17361", "x22015", "x13193", "x15215"])
1119

    
1120
  @staticmethod
1121
  def _GenNestedFilter(op, depth):
1122
    nested = ["=", "name", "value"]
1123
    for i in range(depth):
1124
      nested = [op, nested]
1125
    return nested
1126

    
1127
  def testCompileFilter(self):
1128
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1129

    
1130
    checks = [
1131
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1132
      ["=", "_unknown_field", "value"],
1133
      self._GenNestedFilter("|", levels_max),
1134
      self._GenNestedFilter("|", levels_max * 3),
1135
      self._GenNestedFilter("!", levels_max),
1136
      ]
1137

    
1138
    for fielddefs in query.ALL_FIELD_LISTS:
1139
      for filter_ in checks:
1140
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1141
                          fielddefs, None, filter_)
1142

    
1143
      for op in ["|", "!"]:
1144
        filter_ = self._GenNestedFilter(op, levels_max - 1)
1145
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1146
                                                      filter_)))
1147

    
1148
  def testQueryInputOrder(self):
1149
    fielddefs = query._PrepareFieldList([
1150
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1151
       None, 0, lambda ctx, item: item["pnode"]),
1152
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1153
       None, 0, lambda ctx, item: item["snode"]),
1154
      ], [])
1155

    
1156
    data = [
1157
      { "pnode": "node1", "snode": "node44", },
1158
      { "pnode": "node30", "snode": "node90", },
1159
      { "pnode": "node25", "snode": "node1", },
1160
      { "pnode": "node20", "snode": "node1", },
1161
      ]
1162

    
1163
    filter_ = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1164

    
1165
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1166
                    filter_=filter_)
1167
    self.assertTrue(q.RequestedNames() is None)
1168
    self.assertFalse(q.RequestedData())
1169
    self.assertEqual(q.Query(data),
1170
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1171
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1172
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1173

    
1174
    # Try again with reversed input data
1175
    self.assertEqual(q.Query(reversed(data)),
1176
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1177
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1178
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1179

    
1180
    # No name field, result must be in incoming order
1181
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1182
                    filter_=filter_)
1183
    self.assertFalse(q.RequestedData())
1184
    self.assertEqual(q.Query(data),
1185
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1186
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1187
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1188
    self.assertEqual(q.OldStyleQuery(data), [
1189
      ["node1", "node44"],
1190
      ["node25", "node1"],
1191
      ["node20", "node1"],
1192
      ])
1193
    self.assertEqual(q.Query(reversed(data)),
1194
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1195
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1196
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1197
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1198
      ["node20", "node1"],
1199
      ["node25", "node1"],
1200
      ["node1", "node44"],
1201
      ])
1202

    
1203
    # Name field, but no sorting, result must be in incoming order
1204
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1205
    self.assertFalse(q.RequestedData())
1206
    self.assertEqual(q.Query(data, sort_by_name=False),
1207
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1208
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1209
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1210
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1211
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1212
      ["node1", "node44"],
1213
      ["node30", "node90"],
1214
      ["node25", "node1"],
1215
      ["node20", "node1"],
1216
      ])
1217
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1218
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1219
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1220
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1221
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1222
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1223
      ["node20", "node1"],
1224
      ["node25", "node1"],
1225
      ["node30", "node90"],
1226
      ["node1", "node44"],
1227
      ])
1228

    
1229
  def testEqualNamesOrder(self):
1230
    fielddefs = query._PrepareFieldList([
1231
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1232
       None, 0, lambda ctx, item: item["pnode"]),
1233
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1234
       None, 0, lambda ctx, item: item["num"]),
1235
      ], [])
1236

    
1237
    data = [
1238
      { "pnode": "node1", "num": 100, },
1239
      { "pnode": "node1", "num": 25, },
1240
      { "pnode": "node2", "num": 90, },
1241
      { "pnode": "node2", "num": 30, },
1242
      ]
1243

    
1244
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1245
                    filter_=["|", ["=", "pnode", "node1"],
1246
                                  ["=", "pnode", "node2"],
1247
                                  ["=", "pnode", "node1"]])
1248
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1249
                     msg="Did not return unique names")
1250
    self.assertFalse(q.RequestedData())
1251
    self.assertEqual(q.Query(data),
1252
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1253
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1254
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1255
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1256
    self.assertEqual(q.Query(data, sort_by_name=False),
1257
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1258
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1259
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1260
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1261

    
1262
    data = [
1263
      { "pnode": "nodeX", "num": 50, },
1264
      { "pnode": "nodeY", "num": 40, },
1265
      { "pnode": "nodeX", "num": 30, },
1266
      { "pnode": "nodeX", "num": 20, },
1267
      { "pnode": "nodeM", "num": 10, },
1268
      ]
1269

    
1270
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1271
                    filter_=["|", ["=", "pnode", "nodeX"],
1272
                                  ["=", "pnode", "nodeY"],
1273
                                  ["=", "pnode", "nodeY"],
1274
                                  ["=", "pnode", "nodeY"],
1275
                                  ["=", "pnode", "nodeM"]])
1276
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1277
                     msg="Did not return unique names")
1278
    self.assertFalse(q.RequestedData())
1279

    
1280
    # First sorted by name, then input order
1281
    self.assertEqual(q.Query(data, sort_by_name=True),
1282
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1283
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1284
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1285
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1286
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1287

    
1288
    # Input order
1289
    self.assertEqual(q.Query(data, sort_by_name=False),
1290
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1291
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1292
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1293
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1294
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1295

    
1296
  def testFilter(self):
1297
    (DK_A, DK_B) = range(1000, 1002)
1298

    
1299
    fielddefs = query._PrepareFieldList([
1300
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1301
       DK_A, 0, lambda ctx, item: item["name"]),
1302
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1303
       DK_B, 0, lambda ctx, item: item["other"]),
1304
      ], [])
1305

    
1306
    data = [
1307
      { "name": "node1", "other": "foo", },
1308
      { "name": "node2", "other": "bar", },
1309
      { "name": "node3", "other": "Hello", },
1310
      ]
1311

    
1312
    # Empty filter
1313
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1314
                    filter_=["|"])
1315
    self.assertTrue(q.RequestedNames() is None)
1316
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1317
    self.assertEqual(q.Query(data), [])
1318

    
1319
    # Normal filter
1320
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1321
                    filter_=["=", "name", "node1"])
1322
    self.assertEqual(q.RequestedNames(), ["node1"])
1323
    self.assertEqual(q.Query(data),
1324
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1325

    
1326
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1327
                    filter_=(["|", ["=", "name", "node1"],
1328
                                   ["=", "name", "node3"]]))
1329
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1330
    self.assertEqual(q.Query(data),
1331
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1332
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1333

    
1334
    # Complex filter
1335
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1336
                    filter_=(["|", ["=", "name", "node1"],
1337
                                   ["|", ["=", "name", "node3"],
1338
                                         ["=", "name", "node2"]],
1339
                                   ["=", "name", "node3"]]))
1340
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1341
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1342
    self.assertEqual(q.Query(data),
1343
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1344
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1345
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1346

    
1347
    # Filter data type mismatch
1348
    for i in [-1, 0, 1, 123, [], None, True, False]:
1349
      self.assertRaises(errors.ParameterError, query.Query,
1350
                        fielddefs, ["name", "other"], namefield="name",
1351
                        filter_=["=", "name", i])
1352

    
1353
    # Negative filter
1354
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1355
                    filter_=["!", ["|", ["=", "name", "node1"],
1356
                                        ["=", "name", "node3"]]])
1357
    self.assertTrue(q.RequestedNames() is None)
1358
    self.assertEqual(q.Query(data),
1359
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1360

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

    
1369
    # Data type
1370
    q = query.Query(fielddefs, [], namefield="name",
1371
                    filter_=["|", ["=", "other", "bar"],
1372
                                  ["=", "name", "foo"]])
1373
    self.assertTrue(q.RequestedNames() is None)
1374
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1375
    self.assertEqual(q.Query(data), [[]])
1376

    
1377
    # Only one data type
1378
    q = query.Query(fielddefs, ["other"], namefield="name",
1379
                    filter_=["=", "other", "bar"])
1380
    self.assertTrue(q.RequestedNames() is None)
1381
    self.assertEqual(q.RequestedData(), set([DK_B]))
1382
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1383

    
1384
    q = query.Query(fielddefs, [], namefield="name",
1385
                    filter_=["=", "other", "bar"])
1386
    self.assertTrue(q.RequestedNames() is None)
1387
    self.assertEqual(q.RequestedData(), set([DK_B]))
1388
    self.assertEqual(q.Query(data), [[]])
1389

    
1390
  def testFilterContains(self):
1391
    fielddefs = query._PrepareFieldList([
1392
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1393
       None, 0, lambda ctx, item: item["name"]),
1394
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1395
       None, 0, lambda ctx, item: item["other"]),
1396
      ], [])
1397

    
1398
    data = [
1399
      { "name": "node2", "other": ["x", "y", "bar"], },
1400
      { "name": "node3", "other": "Hello", },
1401
      { "name": "node1", "other": ["a", "b", "foo"], },
1402
      { "name": "empty", "other": []},
1403
      ]
1404

    
1405
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1406
                    filter_=["=[]", "other", "bar"])
1407
    self.assertTrue(q.RequestedNames() is None)
1408
    self.assertEqual(q.Query(data), [
1409
      [(constants.RS_NORMAL, "node2"),
1410
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1411
      ])
1412

    
1413
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1414
                    filter_=["|", ["=[]", "other", "bar"],
1415
                                  ["=[]", "other", "a"],
1416
                                  ["=[]", "other", "b"]])
1417
    self.assertTrue(q.RequestedNames() is None)
1418
    self.assertEqual(q.Query(data), [
1419
      [(constants.RS_NORMAL, "node1"),
1420
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1421
      [(constants.RS_NORMAL, "node2"),
1422
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1423
      ])
1424
    self.assertEqual(q.OldStyleQuery(data), [
1425
      ["node1", ["a", "b", "foo"]],
1426
      ["node2", ["x", "y", "bar"]],
1427
      ])
1428

    
1429
    # Boolean test
1430
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1431
                    filter_=["?", "other"])
1432
    self.assertEqual(q.OldStyleQuery(data), [
1433
      ["node1", ["a", "b", "foo"]],
1434
      ["node2", ["x", "y", "bar"]],
1435
      ["node3", "Hello"],
1436
      ])
1437

    
1438
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1439
                    filter_=["!", ["?", "other"]])
1440
    self.assertEqual(q.OldStyleQuery(data), [
1441
      ["empty", []],
1442
      ])
1443

    
1444
  def testFilterHostname(self):
1445
    fielddefs = query._PrepareFieldList([
1446
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1447
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1448
      ], [])
1449

    
1450
    data = [
1451
      { "name": "node1.example.com", },
1452
      { "name": "node2.example.com", },
1453
      { "name": "node2.example.net", },
1454
      ]
1455

    
1456
    q = query.Query(fielddefs, ["name"], namefield="name",
1457
                    filter_=["=", "name", "node2"])
1458
    self.assertEqual(q.RequestedNames(), ["node2"])
1459
    self.assertEqual(q.Query(data), [
1460
      [(constants.RS_NORMAL, "node2.example.com")],
1461
      [(constants.RS_NORMAL, "node2.example.net")],
1462
      ])
1463

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

    
1471
    q = query.Query(fielddefs, ["name"], namefield="name",
1472
                    filter_=["=", "name", "othername"])
1473
    self.assertEqual(q.RequestedNames(), ["othername"])
1474
    self.assertEqual(q.Query(data), [])
1475

    
1476
    q = query.Query(fielddefs, ["name"], namefield="name",
1477
                    filter_=["|", ["=", "name", "node1.example.com"],
1478
                                  ["=", "name", "node2"]])
1479
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1480
    self.assertEqual(q.Query(data), [
1481
      [(constants.RS_NORMAL, "node1.example.com")],
1482
      [(constants.RS_NORMAL, "node2.example.com")],
1483
      [(constants.RS_NORMAL, "node2.example.net")],
1484
      ])
1485
    self.assertEqual(q.OldStyleQuery(data), [
1486
      ["node1.example.com"],
1487
      ["node2.example.com"],
1488
      ["node2.example.net"],
1489
      ])
1490

    
1491
    q = query.Query(fielddefs, ["name"], namefield="name",
1492
                    filter_=["!=", "name", "node1"])
1493
    self.assertTrue(q.RequestedNames() is None)
1494
    self.assertEqual(q.Query(data), [
1495
      [(constants.RS_NORMAL, "node2.example.com")],
1496
      [(constants.RS_NORMAL, "node2.example.net")],
1497
      ])
1498
    self.assertEqual(q.OldStyleQuery(data), [
1499
      ["node2.example.com"],
1500
      ["node2.example.net"],
1501
      ])
1502

    
1503
  def testFilterBoolean(self):
1504
    fielddefs = query._PrepareFieldList([
1505
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1506
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1507
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1508
       None, 0, lambda ctx, item: item["value"]),
1509
      ], [])
1510

    
1511
    data = [
1512
      { "name": "node1", "value": False, },
1513
      { "name": "node2", "value": True, },
1514
      { "name": "node3", "value": True, },
1515
      ]
1516

    
1517
    q = query.Query(fielddefs, ["name", "value"],
1518
                    filter_=["|", ["=", "value", False],
1519
                                  ["=", "value", True]])
1520
    self.assertTrue(q.RequestedNames() is None)
1521
    self.assertEqual(q.Query(data), [
1522
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1523
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1524
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1525
      ])
1526

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

    
1537
    # Comparing bool with string
1538
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1539
      self.assertRaises(errors.ParameterError, query.Query,
1540
                        fielddefs, ["name", "value"],
1541
                        filter_=["=", "value", i])
1542

    
1543
    # Truth filter
1544
    q = query.Query(fielddefs, ["name", "value"], filter_=["?", "value"])
1545
    self.assertTrue(q.RequestedNames() is None)
1546
    self.assertEqual(q.Query(data), [
1547
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1548
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1549
      ])
1550

    
1551
    # Negative bool filter
1552
    q = query.Query(fielddefs, ["name", "value"], filter_=["!", ["?", "value"]])
1553
    self.assertTrue(q.RequestedNames() is None)
1554
    self.assertEqual(q.Query(data), [
1555
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1556
      ])
1557

    
1558
    # Complex truth filter
1559
    q = query.Query(fielddefs, ["name", "value"],
1560
                    filter_=["|", ["&", ["=", "name", "node1"],
1561
                                        ["!", ["?", "value"]]],
1562
                                  ["?", "value"]])
1563
    self.assertTrue(q.RequestedNames() is None)
1564
    self.assertEqual(q.Query(data), [
1565
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1566
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1567
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1568
      ])
1569

    
1570
  def testFilterRegex(self):
1571
    fielddefs = query._PrepareFieldList([
1572
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1573
       None, 0, lambda ctx, item: item["name"]),
1574
      ], [])
1575

    
1576
    data = [
1577
      { "name": "node1.example.com", },
1578
      { "name": "node2.site.example.com", },
1579
      { "name": "node2.example.net", },
1580

    
1581
      # Empty name
1582
      { "name": "", },
1583
      ]
1584

    
1585
    q = query.Query(fielddefs, ["name"], namefield="name",
1586
                    filter_=["=~", "name", "site"])
1587
    self.assertTrue(q.RequestedNames() is None)
1588
    self.assertEqual(q.Query(data), [
1589
      [(constants.RS_NORMAL, "node2.site.example.com")],
1590
      ])
1591

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

    
1600
    q = query.Query(fielddefs, ["name"], namefield="name",
1601
                    filter_=["=~", "name", r"(?i)\.COM$"])
1602
    self.assertTrue(q.RequestedNames() is None)
1603
    self.assertEqual(q.Query(data), [
1604
      [(constants.RS_NORMAL, "node1.example.com")],
1605
      [(constants.RS_NORMAL, "node2.site.example.com")],
1606
      ])
1607

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

    
1617
    q = query.Query(fielddefs, ["name"], namefield="name",
1618
                    filter_=["=~", "name", r"^$"])
1619
    self.assertTrue(q.RequestedNames() is None)
1620
    self.assertEqual(q.Query(data), [
1621
      [(constants.RS_NORMAL, "")],
1622
      ])
1623

    
1624
    # Invalid regular expression
1625
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1626
                      filter_=["=~", "name", r"["])
1627

    
1628

    
1629
if __name__ == "__main__":
1630
  testutils.GanetiTestProgram()