Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 2c758845

History | View | Annotate | Download (67.2 kB)

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

    
4
# Copyright (C) 2010, 2011, 2012 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
    cluster = objects.Cluster(cluster_name="testcluster",
326
                              ndparams=constants.NDC_DEFAULTS.copy())
327
    grp1 = objects.NodeGroup(name="default",
328
                             uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
329
                             alloc_policy=constants.ALLOC_POLICY_PREFERRED,
330
                             ipolicy=objects.MakeEmptyIPolicy(),
331
                             ndparams={},
332
                             )
333
    grp2 = objects.NodeGroup(name="group2",
334
                             uuid="c0e89160-18e7-11e0-a46e-001d0904babe",
335
                             alloc_policy=constants.ALLOC_POLICY_PREFERRED,
336
                             ipolicy=objects.MakeEmptyIPolicy(),
337
                             ndparams={constants.ND_SPINDLE_COUNT: 2},
338
                             )
339
    groups = {grp1.uuid: grp1, grp2.uuid: grp2}
340
    nodes = [
341
      objects.Node(name="node1", drained=False, group=grp1.uuid, ndparams={}),
342
      objects.Node(name="node2", drained=True, group=grp2.uuid, ndparams={}),
343
      objects.Node(name="node3", drained=False, group=grp1.uuid,
344
                   ndparams={constants.ND_SPINDLE_COUNT: 4}),
345
      ]
346
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
347
      nqd = query.NodeQueryData(nodes, live_data, None, None, None,
348
                                groups, None, cluster)
349

    
350
      q = self._Create(["name", "drained"])
351
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
352
      self.assertEqual(q.Query(nqd),
353
                       [[(constants.RS_NORMAL, "node1"),
354
                         (constants.RS_NORMAL, False)],
355
                        [(constants.RS_NORMAL, "node2"),
356
                         (constants.RS_NORMAL, True)],
357
                        [(constants.RS_NORMAL, "node3"),
358
                         (constants.RS_NORMAL, False)],
359
                       ])
360
      self.assertEqual(q.OldStyleQuery(nqd),
361
                       [["node1", False],
362
                        ["node2", True],
363
                        ["node3", False]])
364
      q = self._Create(["ndp/spindle_count"])
365
      self.assertEqual(q.RequestedData(), set([query.NQ_GROUP]))
366
      self.assertEqual(q.Query(nqd),
367
                       [[(constants.RS_NORMAL,
368
                          constants.NDC_DEFAULTS[constants.ND_SPINDLE_COUNT])],
369
                        [(constants.RS_NORMAL,
370
                          grp2.ndparams[constants.ND_SPINDLE_COUNT])],
371
                        [(constants.RS_NORMAL,
372
                          nodes[2].ndparams[constants.ND_SPINDLE_COUNT])],
373
                       ])
374

    
375
  def test(self):
376
    selected = query.NODE_FIELDS.keys()
377
    field_index = dict((field, idx) for idx, field in enumerate(selected))
378

    
379
    q = self._Create(selected)
380
    self.assertEqual(q.RequestedData(),
381
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
382
                          query.NQ_GROUP, query.NQ_OOB]))
383

    
384
    cluster = objects.Cluster(cluster_name="testcluster",
385
      hvparams=constants.HVC_DEFAULTS,
386
      beparams={
387
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
388
        },
389
      nicparams={
390
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
391
        },
392
      ndparams=constants.NDC_DEFAULTS,
393
        )
394

    
395
    node_names = ["node%s" % i for i in range(20)]
396
    master_name = node_names[3]
397
    nodes = [
398
      objects.Node(name=name,
399
                   primary_ip="192.0.2.%s" % idx,
400
                   secondary_ip="192.0.100.%s" % idx,
401
                   serial_no=7789 * idx,
402
                   master_candidate=(name != master_name and idx % 3 == 0),
403
                   offline=False,
404
                   drained=False,
405
                   powered=True,
406
                   vm_capable=True,
407
                   master_capable=False,
408
                   ndparams={},
409
                   group="default",
410
                   ctime=1290006900,
411
                   mtime=1290006913,
412
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
413
      for idx, name in enumerate(node_names)
414
      ]
415

    
416
    master_node = nodes[3]
417
    master_node.AddTag("masternode")
418
    master_node.AddTag("another")
419
    master_node.AddTag("tag")
420
    master_node.ctime = None
421
    master_node.mtime = None
422
    assert master_node.name == master_name
423

    
424
    live_data_name = node_names[4]
425
    assert live_data_name != master_name
426

    
427
    fake_live_data = {
428
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
429
      "cnodes": 4,
430
      "csockets": 4,
431
      "ctotal": 8,
432
      "mnode": 128,
433
      "mfree": 100,
434
      "mtotal": 4096,
435
      "dfree": 5 * 1024 * 1024,
436
      "dtotal": 100 * 1024 * 1024,
437
      }
438

    
439
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
440
            sorted(fake_live_data.keys()))
441

    
442
    live_data = dict.fromkeys(node_names, {})
443
    live_data[live_data_name] = \
444
      dict((query._NODE_LIVE_FIELDS[name][2], value)
445
           for name, value in fake_live_data.items())
446

    
447
    node_to_primary = dict((name, set()) for name in node_names)
448
    node_to_primary[master_name].update(["inst1", "inst2"])
449

    
450
    node_to_secondary = dict((name, set()) for name in node_names)
451
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
452

    
453
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
454
    groups = {
455
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
456
      }
457

    
458
    oob_not_powered_node = node_names[0]
459
    nodes[0].powered = False
460
    oob_support = dict((name, False) for name in node_names)
461
    oob_support[master_name] = True
462
    oob_support[oob_not_powered_node] = True
463

    
464
    master_node.group = ng_uuid
465

    
466
    nqd = query.NodeQueryData(nodes, live_data, master_name,
467
                              node_to_primary, node_to_secondary, groups,
468
                              oob_support, cluster)
469
    result = q.Query(nqd)
470
    self.assert_(compat.all(len(row) == len(selected) for row in result))
471
    self.assertEqual([row[field_index["name"]] for row in result],
472
                     [(constants.RS_NORMAL, name) for name in node_names])
473

    
474
    node_to_row = dict((row[field_index["name"]][1], idx)
475
                       for idx, row in enumerate(result))
476

    
477
    master_row = result[node_to_row[master_name]]
478
    self.assert_(master_row[field_index["master"]])
479
    self.assert_(master_row[field_index["role"]], "M")
480
    self.assertEqual(master_row[field_index["group"]],
481
                     (constants.RS_NORMAL, "ng1"))
482
    self.assertEqual(master_row[field_index["group.uuid"]],
483
                     (constants.RS_NORMAL, ng_uuid))
484
    self.assertEqual(master_row[field_index["ctime"]],
485
                     (constants.RS_UNAVAIL, None))
486
    self.assertEqual(master_row[field_index["mtime"]],
487
                     (constants.RS_UNAVAIL, None))
488

    
489
    self.assert_(row[field_index["pip"]] == node.primary_ip and
490
                 row[field_index["sip"]] == node.secondary_ip and
491
                 set(row[field_index["tags"]]) == node.GetTags() and
492
                 row[field_index["serial_no"]] == node.serial_no and
493
                 row[field_index["role"]] == query._GetNodeRole(node,
494
                                                                master_name) and
495
                 (node.name == master_name or
496
                  (row[field_index["group"]] == "<unknown>" and
497
                   row[field_index["group.uuid"]] is None and
498
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
499
                                                 node.ctime) and
500
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
501
                                                 node.mtime) and
502
                   row[field_index["powered"]] == (constants.RS_NORMAL,
503
                                                   True))) or
504
                 (node.name == oob_not_powered_node and
505
                  row[field_index["powered"]] == (constants.RS_NORMAL,
506
                                                  False)) or
507
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
508
                 for row, node in zip(result, nodes))
509

    
510
    live_data_row = result[node_to_row[live_data_name]]
511

    
512
    for (field, value) in fake_live_data.items():
513
      self.assertEqual(live_data_row[field_index[field]],
514
                       (constants.RS_NORMAL, value))
515

    
516
    self.assertEqual(master_row[field_index["pinst_cnt"]],
517
                     (constants.RS_NORMAL, 2))
518
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
519
                     (constants.RS_NORMAL, 3))
520
    self.assertEqual(master_row[field_index["pinst_list"]],
521
                     (constants.RS_NORMAL,
522
                      list(node_to_primary[master_name])))
523
    self.assertEqual(live_data_row[field_index["sinst_list"]],
524
                     (constants.RS_NORMAL,
525
                      list(node_to_secondary[live_data_name])))
526

    
527
  def testGetLiveNodeField(self):
528
    nodes = [
529
      objects.Node(name="node1", drained=False, offline=False,
530
                   vm_capable=True),
531
      objects.Node(name="node2", drained=True, offline=False,
532
                   vm_capable=True),
533
      objects.Node(name="node3", drained=False, offline=False,
534
                   vm_capable=True),
535
      objects.Node(name="node4", drained=False, offline=True,
536
                   vm_capable=True),
537
      objects.Node(name="node5", drained=False, offline=False,
538
                   vm_capable=False),
539
      ]
540
    live_data = dict.fromkeys([node.name for node in nodes], {})
541

    
542
    # No data
543
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
544
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
545
                                             nqd, nodes[0]),
546
                     query._FS_NODATA)
547

    
548
    # Missing field
549
    ctx = _QueryData(None, curlive_data={
550
      "some": 1,
551
      "other": 2,
552
      })
553
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
554
                                             ctx, nodes[0]),
555
                     query._FS_UNAVAIL)
556

    
557
    # Wrong format/datatype
558
    ctx = _QueryData(None, curlive_data={
559
      "hello": ["Hello World"],
560
      "other": 2,
561
      })
562
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
563
                                             ctx, nodes[0]),
564
                     query._FS_UNAVAIL)
565

    
566
    # Offline node
567
    assert nodes[3].offline
568
    ctx = _QueryData(None, curlive_data={})
569
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
570
                                             ctx, nodes[3]),
571
                     query._FS_OFFLINE, None)
572

    
573
    # Wrong field type
574
    ctx = _QueryData(None, curlive_data={"hello": 123})
575
    self.assertRaises(AssertionError, query._GetLiveNodeField,
576
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
577

    
578
    # Non-vm_capable node
579
    assert not nodes[4].vm_capable
580
    ctx = _QueryData(None, curlive_data={})
581
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
582
                                             ctx, nodes[4]),
583
                     query._FS_UNAVAIL, None)
584

    
585

    
586
class TestInstanceQuery(unittest.TestCase):
587
  def _Create(self, selected):
588
    return query.Query(query.INSTANCE_FIELDS, selected)
589

    
590
  def testSimple(self):
591
    q = self._Create(["name", "be/maxmem", "ip"])
592
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
593

    
594
    cluster = objects.Cluster(cluster_name="testcluster",
595
      hvparams=constants.HVC_DEFAULTS,
596
      beparams={
597
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
598
        },
599
      nicparams={
600
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
601
        },
602
      os_hvp={},
603
      osparams={})
604

    
605
    instances = [
606
      objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
607
                       nics=[], os="deb1"),
608
      objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
609
        os="foomoo",
610
        beparams={
611
          constants.BE_MAXMEM: 512,
612
        }),
613
      objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
614
        os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
615
      ]
616

    
617
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
618
                                  set(), {}, None, None)
619
    self.assertEqual(q.Query(iqd),
620
      [[(constants.RS_NORMAL, "inst1"),
621
        (constants.RS_NORMAL, 128),
622
        (constants.RS_UNAVAIL, None),
623
       ],
624
       [(constants.RS_NORMAL, "inst2"),
625
        (constants.RS_NORMAL, 512),
626
        (constants.RS_UNAVAIL, None),
627
       ],
628
       [(constants.RS_NORMAL, "inst3"),
629
        (constants.RS_NORMAL, 128),
630
        (constants.RS_NORMAL, "192.0.2.99"),
631
       ]])
632
    self.assertEqual(q.OldStyleQuery(iqd),
633
      [["inst1", 128, None],
634
       ["inst2", 512, None],
635
       ["inst3", 128, "192.0.2.99"]])
636

    
637
  def test(self):
638
    selected = query.INSTANCE_FIELDS.keys()
639
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
640

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

    
644
    q = self._Create(selected)
645
    self.assertEqual(q.RequestedData(),
646
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
647
                          query.IQ_CONSOLE, query.IQ_NODES]))
648

    
649
    cluster = objects.Cluster(cluster_name="testcluster",
650
      hvparams=constants.HVC_DEFAULTS,
651
      beparams={
652
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
653
        },
654
      nicparams={
655
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
656
        },
657
      os_hvp={},
658
      tcpudp_port_pool=set(),
659
      osparams={
660
        "deb99": {
661
          "clean_install": "yes",
662
          },
663
        })
664

    
665
    offline_nodes = ["nodeoff1", "nodeoff2"]
666
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
667
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
668

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

    
783
    assert not utils.FindDuplicates(inst.name for inst in instances)
784

    
785
    instbyname = dict((inst.name, inst) for inst in instances)
786

    
787
    disk_usage = dict((inst.name,
788
                       cmdlib._ComputeDiskSize(inst.disk_template,
789
                                               [{"size": disk.size}
790
                                                for disk in inst.disks]))
791
                      for inst in instances)
792

    
793
    inst_bridges = {
794
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
795
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
796
                None, "eth123"],
797
      }
798

    
799
    live_data = {
800
      "inst2": {
801
        "vcpus": 3,
802
        },
803
      "inst4": {
804
        "memory": 123,
805
        },
806
      "inst6": {
807
        "memory": 768,
808
        },
809
      "inst7": {
810
        "vcpus": 3,
811
        },
812
      }
813
    wrongnode_inst = set(["inst7"])
814

    
815
    consinfo = dict((inst.name, None) for inst in instances)
816
    consinfo["inst7"] = \
817
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
818
                              host=instbyname["inst7"].primary_node,
819
                              user=constants.GANETI_RUNAS,
820
                              command=["hostname"]).ToDict()
821

    
822
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
823
                                  offline_nodes, bad_nodes, live_data,
824
                                  wrongnode_inst, consinfo, {}, {})
825
    result = q.Query(iqd)
826
    self.assertEqual(len(result), len(instances))
827
    self.assert_(compat.all(len(row) == len(selected)
828
                            for row in result))
829

    
830
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
831
           "Offline nodes not included in bad nodes"
832

    
833
    tested_status = set()
834

    
835
    for (inst, row) in zip(instances, result):
836
      assert inst.primary_node in nodes
837

    
838
      self.assertEqual(row[fieldidx["name"]],
839
                       (constants.RS_NORMAL, inst.name))
840

    
841
      if inst.primary_node in offline_nodes:
842
        exp_status = constants.INSTST_NODEOFFLINE
843
      elif inst.primary_node in bad_nodes:
844
        exp_status = constants.INSTST_NODEDOWN
845
      elif inst.name in live_data:
846
        if inst.name in wrongnode_inst:
847
          exp_status = constants.INSTST_WRONGNODE
848
        elif inst.admin_state == constants.ADMINST_UP:
849
          exp_status = constants.INSTST_RUNNING
850
        else:
851
          exp_status = constants.INSTST_ERRORUP
852
      elif inst.admin_state == constants.ADMINST_UP:
853
        exp_status = constants.INSTST_ERRORDOWN
854
      elif inst.admin_state == constants.ADMINST_DOWN:
855
        exp_status = constants.INSTST_ADMINDOWN
856
      else:
857
        exp_status = constants.INSTST_ADMINOFFLINE
858

    
859
      self.assertEqual(row[fieldidx["status"]],
860
                       (constants.RS_NORMAL, exp_status))
861

    
862
      (_, status) = row[fieldidx["status"]]
863
      tested_status.add(status)
864

    
865
      #FIXME(dynmem): check oper_ram vs min/max mem
866
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
867
        if inst.primary_node in bad_nodes:
868
          exp = (constants.RS_NODATA, None)
869
        elif inst.name in live_data:
870
          value = live_data[inst.name].get(livefield, None)
871
          if value is None:
872
            exp = (constants.RS_UNAVAIL, None)
873
          else:
874
            exp = (constants.RS_NORMAL, value)
875
        else:
876
          exp = (constants.RS_UNAVAIL, None)
877

    
878
        self.assertEqual(row[fieldidx[field]], exp)
879

    
880
      bridges = inst_bridges.get(inst.name, [])
881
      self.assertEqual(row[fieldidx["nic.bridges"]],
882
                       (constants.RS_NORMAL, bridges))
883
      if bridges:
884
        self.assertEqual(row[fieldidx["bridge"]],
885
                         (constants.RS_NORMAL, bridges[0]))
886
      else:
887
        self.assertEqual(row[fieldidx["bridge"]],
888
                         (constants.RS_UNAVAIL, None))
889

    
890
      for i in range(constants.MAX_NICS):
891
        if i < len(bridges) and bridges[i] is not None:
892
          exp = (constants.RS_NORMAL, bridges[i])
893
        else:
894
          exp = (constants.RS_UNAVAIL, None)
895
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
896

    
897
      if inst.primary_node in bad_nodes:
898
        exp = (constants.RS_NODATA, None)
899
      else:
900
        exp = (constants.RS_NORMAL, inst.name in live_data)
901
      self.assertEqual(row[fieldidx["oper_state"]], exp)
902

    
903
      cust_exp = (constants.RS_NORMAL, {})
904
      if inst.os == "deb99":
905
        if inst.name == "inst6":
906
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
907
          cust_exp = exp
908
        else:
909
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
910
      else:
911
        exp = (constants.RS_NORMAL, {})
912
      self.assertEqual(row[fieldidx["osparams"]], exp)
913
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
914

    
915
      usage = disk_usage[inst.name]
916
      if usage is None:
917
        usage = 0
918
      self.assertEqual(row[fieldidx["disk_usage"]],
919
                       (constants.RS_NORMAL, usage))
920

    
921
      for alias, target in [("sda_size", "disk.size/0"),
922
                            ("sdb_size", "disk.size/1"),
923
                            ("vcpus", "be/vcpus"),
924
                            ("ip", "nic.ip/0"),
925
                            ("mac", "nic.mac/0"),
926
                            ("bridge", "nic.bridge/0"),
927
                            ("nic_mode", "nic.mode/0"),
928
                            ("nic_link", "nic.link/0"),
929
                            ]:
930
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
931

    
932
      for field in ["ctime", "mtime"]:
933
        if getattr(inst, field) is None:
934
          # No ctime/mtime
935
          exp = (constants.RS_UNAVAIL, None)
936
        else:
937
          exp = (constants.RS_NORMAL, getattr(inst, field))
938
        self.assertEqual(row[fieldidx[field]], exp)
939

    
940
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
941

    
942
    # Ensure all possible status' have been tested
943
    self.assertEqual(tested_status, constants.INSTST_ALL)
944

    
945
  def _CheckInstanceConsole(self, instance, (status, consdata)):
946
    if instance.name == "inst7":
947
      self.assertEqual(status, constants.RS_NORMAL)
948
      console = objects.InstanceConsole.FromDict(consdata)
949
      self.assertTrue(console.Validate())
950
      self.assertEqual(console.host, instance.primary_node)
951
    else:
952
      self.assertEqual(status, constants.RS_UNAVAIL)
953

    
954

    
955
class TestGroupQuery(unittest.TestCase):
956

    
957
  def setUp(self):
958
    self.groups = [
959
      objects.NodeGroup(name="default",
960
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
961
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED,
962
                        ipolicy=objects.MakeEmptyIPolicy(),
963
                        ndparams={},
964
                        diskparams={},
965
                        ),
966
      objects.NodeGroup(name="restricted",
967
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
968
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
969
                        ipolicy=objects.MakeEmptyIPolicy(),
970
                        ndparams={},
971
                        diskparams={},
972
                        ),
973
      ]
974
    self.cluster = objects.Cluster(cluster_name="testcluster",
975
      hvparams=constants.HVC_DEFAULTS,
976
      beparams={
977
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
978
        },
979
      nicparams={
980
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
981
        },
982
      ndparams=constants.NDC_DEFAULTS,
983
      ipolicy=constants.IPOLICY_DEFAULTS,
984
      diskparams=constants.DISK_DT_DEFAULTS,
985
      )
986

    
987
  def _Create(self, selected):
988
    return query.Query(query.GROUP_FIELDS, selected)
989

    
990
  def testSimple(self):
991
    q = self._Create(["name", "uuid", "alloc_policy"])
992
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
993

    
994
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
995

    
996
    self.assertEqual(q.Query(gqd),
997
      [[(constants.RS_NORMAL, "default"),
998
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
999
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1000
        ],
1001
       [(constants.RS_NORMAL, "restricted"),
1002
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1003
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1004
        ],
1005
       ])
1006

    
1007
  def testNodes(self):
1008
    groups_to_nodes = {
1009
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1010
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1011
      }
1012

    
1013
    q = self._Create(["name", "node_cnt", "node_list"])
1014
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1015
                               False)
1016

    
1017
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1018

    
1019
    self.assertEqual(q.Query(gqd),
1020
                     [[(constants.RS_NORMAL, "default"),
1021
                       (constants.RS_NORMAL, 2),
1022
                       (constants.RS_NORMAL, ["node1", "node2"]),
1023
                       ],
1024
                      [(constants.RS_NORMAL, "restricted"),
1025
                       (constants.RS_NORMAL, 3),
1026
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1027
                       ],
1028
                      ])
1029

    
1030
  def testInstances(self):
1031
    groups_to_instances = {
1032
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1033
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1034
      }
1035

    
1036
    q = self._Create(["pinst_cnt", "pinst_list"])
1037
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1038
      groups_to_instances, False)
1039

    
1040
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1041

    
1042
    self.assertEqual(q.Query(gqd),
1043
                     [[(constants.RS_NORMAL, 2),
1044
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1045
                       ],
1046
                      [(constants.RS_NORMAL, 3),
1047
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1048
                       ],
1049
                      ])
1050

    
1051

    
1052
class TestOsQuery(unittest.TestCase):
1053
  def _Create(self, selected):
1054
    return query.Query(query.OS_FIELDS, selected)
1055

    
1056
  def test(self):
1057
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1058
    api_versions = [10, 0, 15, 5]
1059
    parameters = ["zpar3", "apar9"]
1060

    
1061
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1062
    assert (api_versions != sorted(api_versions) and
1063
            api_versions != utils.NiceSort(variants))
1064
    assert (parameters != sorted(parameters) and
1065
            parameters != utils.NiceSort(parameters))
1066

    
1067
    data = [
1068
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1069
                   variants=set(), api_versions=set(), parameters=set(),
1070
                   node_status={ "some": "status", }),
1071
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1072
                   variants=set(variants),
1073
                   api_versions=set(api_versions),
1074
                   parameters=set(parameters),
1075
                   node_status={ "some": "other", "status": None, }),
1076
      ]
1077

    
1078

    
1079
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1080
                      "api_versions", "parameters", "node_status"])
1081
    self.assertEqual(q.RequestedData(), set([]))
1082
    self.assertEqual(q.Query(data),
1083
                     [[(constants.RS_NORMAL, "debian"),
1084
                       (constants.RS_NORMAL, False),
1085
                       (constants.RS_NORMAL, False),
1086
                       (constants.RS_NORMAL, False),
1087
                       (constants.RS_NORMAL, []),
1088
                       (constants.RS_NORMAL, []),
1089
                       (constants.RS_NORMAL, []),
1090
                       (constants.RS_NORMAL, {"some": "status"})],
1091
                      [(constants.RS_NORMAL, "dos"),
1092
                       (constants.RS_NORMAL, True),
1093
                       (constants.RS_NORMAL, False),
1094
                       (constants.RS_NORMAL, True),
1095
                       (constants.RS_NORMAL,
1096
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1097
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1098
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1099
                       (constants.RS_NORMAL,
1100
                        { "some": "other", "status": None, })
1101
                       ]])
1102

    
1103

    
1104
class TestQueryFields(unittest.TestCase):
1105
  def testAllFields(self):
1106
    for fielddefs in query.ALL_FIELD_LISTS:
1107
      result = query.QueryFields(fielddefs, None)
1108
      self.assert_(isinstance(result, dict))
1109
      response = objects.QueryFieldsResponse.FromDict(result)
1110
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1111
        [(fdef2.name, fdef2.title)
1112
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1113
                                                key=lambda x: x[0].name)])
1114

    
1115
  def testSomeFields(self):
1116
    rnd = random.Random(5357)
1117

    
1118
    for _ in range(10):
1119
      for fielddefs in query.ALL_FIELD_LISTS:
1120
        if len(fielddefs) > 20:
1121
          sample_size = rnd.randint(5, 20)
1122
        else:
1123
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1124
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1125
                                                         sample_size)]
1126
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1127
        self.assert_(isinstance(result, dict))
1128
        response = objects.QueryFieldsResponse.FromDict(result)
1129
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1130
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1131

    
1132

    
1133
class TestQueryFilter(unittest.TestCase):
1134
  def testRequestedNames(self):
1135
    for (what, fielddefs) in query.ALL_FIELDS.items():
1136
      if what == constants.QR_JOB:
1137
        namefield = "id"
1138
      elif what == constants.QR_EXPORT:
1139
        namefield = "export"
1140
      else:
1141
        namefield = "name"
1142

    
1143
      assert namefield in fielddefs
1144

    
1145
      innerfilter = [["=", namefield, "x%s" % i] for i in range(4)]
1146

    
1147
      # No name field
1148
      q = query.Query(fielddefs, [namefield], qfilter=["=", namefield, "abc"],
1149
                      namefield=None)
1150
      self.assertEqual(q.RequestedNames(), None)
1151

    
1152
      # No filter
1153
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1154
      self.assertEqual(q.RequestedNames(), None)
1155

    
1156
      # Check empty query
1157
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1158
                      namefield=namefield)
1159
      self.assertEqual(q.RequestedNames(), None)
1160

    
1161
      # Check order
1162
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1163
                      namefield=namefield)
1164
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1165

    
1166
      # Check reverse order
1167
      q = query.Query(fielddefs, [namefield],
1168
                      qfilter=["|"] + list(reversed(innerfilter)),
1169
                      namefield=namefield)
1170
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1171

    
1172
      # Duplicates
1173
      q = query.Query(fielddefs, [namefield],
1174
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1175
                      namefield=namefield)
1176
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1177

    
1178
      # Unknown name field
1179
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1180
                        namefield="_unknown_field_")
1181

    
1182
      # Filter with AND
1183
      q = query.Query(fielddefs, [namefield],
1184
                      qfilter=["|", ["=", namefield, "foo"],
1185
                                    ["&", ["=", namefield, ""]]],
1186
                      namefield=namefield)
1187
      self.assertTrue(q.RequestedNames() is None)
1188

    
1189
      # Filter with NOT
1190
      q = query.Query(fielddefs, [namefield],
1191
                      qfilter=["|", ["=", namefield, "foo"],
1192
                                    ["!", ["=", namefield, ""]]],
1193
                      namefield=namefield)
1194
      self.assertTrue(q.RequestedNames() is None)
1195

    
1196
      # Filter with only OR (names must be in correct order)
1197
      q = query.Query(fielddefs, [namefield],
1198
                      qfilter=["|", ["=", namefield, "x17361"],
1199
                                    ["|", ["=", namefield, "x22015"]],
1200
                                    ["|", ["|", ["=", namefield, "x13193"]]],
1201
                                    ["=", namefield, "x15215"]],
1202
                      namefield=namefield)
1203
      self.assertEqual(q.RequestedNames(),
1204
                       ["x17361", "x22015", "x13193", "x15215"])
1205

    
1206
  @staticmethod
1207
  def _GenNestedFilter(namefield, op, depth):
1208
    nested = ["=", namefield, "value"]
1209
    for i in range(depth):
1210
      nested = [op, nested]
1211
    return nested
1212

    
1213
  def testCompileFilter(self):
1214
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1215

    
1216
    for (what, fielddefs) in query.ALL_FIELDS.items():
1217
      if what == constants.QR_JOB:
1218
        namefield = "id"
1219
      elif what == constants.QR_EXPORT:
1220
        namefield = "export"
1221
      else:
1222
        namefield = "name"
1223

    
1224
      checks = [
1225
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1226
        ["=", "_unknown_field", "value"],
1227
        self._GenNestedFilter(namefield, "|", levels_max),
1228
        self._GenNestedFilter(namefield, "|", levels_max * 3),
1229
        self._GenNestedFilter(namefield, "!", levels_max),
1230
        ]
1231

    
1232
      for qfilter in checks:
1233
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1234
                          fielddefs, None, qfilter)
1235

    
1236
      for op in ["|", "!"]:
1237
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1)
1238
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1239
                                                      qfilter)))
1240

    
1241
  def testQueryInputOrder(self):
1242
    fielddefs = query._PrepareFieldList([
1243
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1244
       None, 0, lambda ctx, item: item["pnode"]),
1245
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1246
       None, 0, lambda ctx, item: item["snode"]),
1247
      ], [])
1248

    
1249
    data = [
1250
      { "pnode": "node1", "snode": "node44", },
1251
      { "pnode": "node30", "snode": "node90", },
1252
      { "pnode": "node25", "snode": "node1", },
1253
      { "pnode": "node20", "snode": "node1", },
1254
      ]
1255

    
1256
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1257

    
1258
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1259
                    qfilter=qfilter)
1260
    self.assertTrue(q.RequestedNames() is None)
1261
    self.assertFalse(q.RequestedData())
1262
    self.assertEqual(q.Query(data),
1263
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1264
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1265
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1266

    
1267
    # Try again with reversed input data
1268
    self.assertEqual(q.Query(reversed(data)),
1269
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1270
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1271
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1272

    
1273
    # No name field, result must be in incoming order
1274
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1275
                    qfilter=qfilter)
1276
    self.assertFalse(q.RequestedData())
1277
    self.assertEqual(q.Query(data),
1278
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1279
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1280
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1281
    self.assertEqual(q.OldStyleQuery(data), [
1282
      ["node1", "node44"],
1283
      ["node25", "node1"],
1284
      ["node20", "node1"],
1285
      ])
1286
    self.assertEqual(q.Query(reversed(data)),
1287
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1288
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1289
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1290
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1291
      ["node20", "node1"],
1292
      ["node25", "node1"],
1293
      ["node1", "node44"],
1294
      ])
1295

    
1296
    # Name field, but no sorting, result must be in incoming order
1297
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1298
    self.assertFalse(q.RequestedData())
1299
    self.assertEqual(q.Query(data, sort_by_name=False),
1300
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1301
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1302
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1303
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1304
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1305
      ["node1", "node44"],
1306
      ["node30", "node90"],
1307
      ["node25", "node1"],
1308
      ["node20", "node1"],
1309
      ])
1310
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1311
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1312
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1313
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1314
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1315
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1316
      ["node20", "node1"],
1317
      ["node25", "node1"],
1318
      ["node30", "node90"],
1319
      ["node1", "node44"],
1320
      ])
1321

    
1322
  def testEqualNamesOrder(self):
1323
    fielddefs = query._PrepareFieldList([
1324
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1325
       None, 0, lambda ctx, item: item["pnode"]),
1326
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1327
       None, 0, lambda ctx, item: item["num"]),
1328
      ], [])
1329

    
1330
    data = [
1331
      { "pnode": "node1", "num": 100, },
1332
      { "pnode": "node1", "num": 25, },
1333
      { "pnode": "node2", "num": 90, },
1334
      { "pnode": "node2", "num": 30, },
1335
      ]
1336

    
1337
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1338
                    qfilter=["|", ["=", "pnode", "node1"],
1339
                                  ["=", "pnode", "node2"],
1340
                                  ["=", "pnode", "node1"]])
1341
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1342
                     msg="Did not return unique names")
1343
    self.assertFalse(q.RequestedData())
1344
    self.assertEqual(q.Query(data),
1345
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1346
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1347
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1348
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1349
    self.assertEqual(q.Query(data, sort_by_name=False),
1350
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1351
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1352
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1353
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1354

    
1355
    data = [
1356
      { "pnode": "nodeX", "num": 50, },
1357
      { "pnode": "nodeY", "num": 40, },
1358
      { "pnode": "nodeX", "num": 30, },
1359
      { "pnode": "nodeX", "num": 20, },
1360
      { "pnode": "nodeM", "num": 10, },
1361
      ]
1362

    
1363
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1364
                    qfilter=["|", ["=", "pnode", "nodeX"],
1365
                                  ["=", "pnode", "nodeY"],
1366
                                  ["=", "pnode", "nodeY"],
1367
                                  ["=", "pnode", "nodeY"],
1368
                                  ["=", "pnode", "nodeM"]])
1369
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1370
                     msg="Did not return unique names")
1371
    self.assertFalse(q.RequestedData())
1372

    
1373
    # First sorted by name, then input order
1374
    self.assertEqual(q.Query(data, sort_by_name=True),
1375
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1376
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1377
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1378
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1379
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1380

    
1381
    # Input order
1382
    self.assertEqual(q.Query(data, sort_by_name=False),
1383
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1384
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1385
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1386
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1387
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1388

    
1389
  def testFilter(self):
1390
    (DK_A, DK_B) = range(1000, 1002)
1391

    
1392
    fielddefs = query._PrepareFieldList([
1393
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1394
       DK_A, 0, lambda ctx, item: item["name"]),
1395
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1396
       DK_B, 0, lambda ctx, item: item["other"]),
1397
      ], [])
1398

    
1399
    data = [
1400
      { "name": "node1", "other": "foo", },
1401
      { "name": "node2", "other": "bar", },
1402
      { "name": "node3", "other": "Hello", },
1403
      ]
1404

    
1405
    # Empty filter
1406
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1407
                    qfilter=["|"])
1408
    self.assertTrue(q.RequestedNames() is None)
1409
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1410
    self.assertEqual(q.Query(data), [])
1411

    
1412
    # Normal filter
1413
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1414
                    qfilter=["=", "name", "node1"])
1415
    self.assertEqual(q.RequestedNames(), ["node1"])
1416
    self.assertEqual(q.Query(data),
1417
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1418

    
1419
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1420
                    qfilter=(["|", ["=", "name", "node1"],
1421
                                   ["=", "name", "node3"]]))
1422
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1423
    self.assertEqual(q.Query(data),
1424
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1425
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1426

    
1427
    # Complex filter
1428
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1429
                    qfilter=(["|", ["=", "name", "node1"],
1430
                                   ["|", ["=", "name", "node3"],
1431
                                         ["=", "name", "node2"]],
1432
                                   ["=", "name", "node3"]]))
1433
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1434
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1435
    self.assertEqual(q.Query(data),
1436
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1437
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1438
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1439

    
1440
    # Filter data type mismatch
1441
    for i in [-1, 0, 1, 123, [], None, True, False]:
1442
      self.assertRaises(errors.ParameterError, query.Query,
1443
                        fielddefs, ["name", "other"], namefield="name",
1444
                        qfilter=["=", "name", i])
1445

    
1446
    # Negative filter
1447
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1448
                    qfilter=["!", ["|", ["=", "name", "node1"],
1449
                                        ["=", "name", "node3"]]])
1450
    self.assertTrue(q.RequestedNames() is None)
1451
    self.assertEqual(q.Query(data),
1452
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1453

    
1454
    # Not equal
1455
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1456
                    qfilter=["!=", "name", "node3"])
1457
    self.assertTrue(q.RequestedNames() is None)
1458
    self.assertEqual(q.Query(data),
1459
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1460
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1461

    
1462
    # Data type
1463
    q = query.Query(fielddefs, [], namefield="name",
1464
                    qfilter=["|", ["=", "other", "bar"],
1465
                                  ["=", "name", "foo"]])
1466
    self.assertTrue(q.RequestedNames() is None)
1467
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1468
    self.assertEqual(q.Query(data), [[]])
1469

    
1470
    # Only one data type
1471
    q = query.Query(fielddefs, ["other"], namefield="name",
1472
                    qfilter=["=", "other", "bar"])
1473
    self.assertTrue(q.RequestedNames() is None)
1474
    self.assertEqual(q.RequestedData(), set([DK_B]))
1475
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1476

    
1477
    q = query.Query(fielddefs, [], namefield="name",
1478
                    qfilter=["=", "other", "bar"])
1479
    self.assertTrue(q.RequestedNames() is None)
1480
    self.assertEqual(q.RequestedData(), set([DK_B]))
1481
    self.assertEqual(q.Query(data), [[]])
1482

    
1483
  def testFilterContains(self):
1484
    fielddefs = query._PrepareFieldList([
1485
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1486
       None, 0, lambda ctx, item: item["name"]),
1487
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1488
       None, 0, lambda ctx, item: item["other"]),
1489
      ], [])
1490

    
1491
    data = [
1492
      { "name": "node2", "other": ["x", "y", "bar"], },
1493
      { "name": "node3", "other": "Hello", },
1494
      { "name": "node1", "other": ["a", "b", "foo"], },
1495
      { "name": "empty", "other": []},
1496
      ]
1497

    
1498
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1499
                    qfilter=["=[]", "other", "bar"])
1500
    self.assertTrue(q.RequestedNames() is None)
1501
    self.assertEqual(q.Query(data), [
1502
      [(constants.RS_NORMAL, "node2"),
1503
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1504
      ])
1505

    
1506
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1507
                    qfilter=["|", ["=[]", "other", "bar"],
1508
                                  ["=[]", "other", "a"],
1509
                                  ["=[]", "other", "b"]])
1510
    self.assertTrue(q.RequestedNames() is None)
1511
    self.assertEqual(q.Query(data), [
1512
      [(constants.RS_NORMAL, "node1"),
1513
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1514
      [(constants.RS_NORMAL, "node2"),
1515
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1516
      ])
1517
    self.assertEqual(q.OldStyleQuery(data), [
1518
      ["node1", ["a", "b", "foo"]],
1519
      ["node2", ["x", "y", "bar"]],
1520
      ])
1521

    
1522
    # Boolean test
1523
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1524
                    qfilter=["?", "other"])
1525
    self.assertEqual(q.OldStyleQuery(data), [
1526
      ["node1", ["a", "b", "foo"]],
1527
      ["node2", ["x", "y", "bar"]],
1528
      ["node3", "Hello"],
1529
      ])
1530

    
1531
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1532
                    qfilter=["!", ["?", "other"]])
1533
    self.assertEqual(q.OldStyleQuery(data), [
1534
      ["empty", []],
1535
      ])
1536

    
1537
  def testFilterHostname(self):
1538
    fielddefs = query._PrepareFieldList([
1539
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1540
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1541
      ], [])
1542

    
1543
    data = [
1544
      { "name": "node1.example.com", },
1545
      { "name": "node2.example.com", },
1546
      { "name": "node2.example.net", },
1547
      ]
1548

    
1549
    q = query.Query(fielddefs, ["name"], namefield="name",
1550
                    qfilter=["=", "name", "node2"])
1551
    self.assertEqual(q.RequestedNames(), ["node2"])
1552
    self.assertEqual(q.Query(data), [
1553
      [(constants.RS_NORMAL, "node2.example.com")],
1554
      [(constants.RS_NORMAL, "node2.example.net")],
1555
      ])
1556

    
1557
    q = query.Query(fielddefs, ["name"], namefield="name",
1558
                    qfilter=["=", "name", "node1"])
1559
    self.assertEqual(q.RequestedNames(), ["node1"])
1560
    self.assertEqual(q.Query(data), [
1561
      [(constants.RS_NORMAL, "node1.example.com")],
1562
      ])
1563

    
1564
    q = query.Query(fielddefs, ["name"], namefield="name",
1565
                    qfilter=["=", "name", "othername"])
1566
    self.assertEqual(q.RequestedNames(), ["othername"])
1567
    self.assertEqual(q.Query(data), [])
1568

    
1569
    q = query.Query(fielddefs, ["name"], namefield="name",
1570
                    qfilter=["|", ["=", "name", "node1.example.com"],
1571
                                  ["=", "name", "node2"]])
1572
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1573
    self.assertEqual(q.Query(data), [
1574
      [(constants.RS_NORMAL, "node1.example.com")],
1575
      [(constants.RS_NORMAL, "node2.example.com")],
1576
      [(constants.RS_NORMAL, "node2.example.net")],
1577
      ])
1578
    self.assertEqual(q.OldStyleQuery(data), [
1579
      ["node1.example.com"],
1580
      ["node2.example.com"],
1581
      ["node2.example.net"],
1582
      ])
1583

    
1584
    q = query.Query(fielddefs, ["name"], namefield="name",
1585
                    qfilter=["!=", "name", "node1"])
1586
    self.assertTrue(q.RequestedNames() is None)
1587
    self.assertEqual(q.Query(data), [
1588
      [(constants.RS_NORMAL, "node2.example.com")],
1589
      [(constants.RS_NORMAL, "node2.example.net")],
1590
      ])
1591
    self.assertEqual(q.OldStyleQuery(data), [
1592
      ["node2.example.com"],
1593
      ["node2.example.net"],
1594
      ])
1595

    
1596
  def testFilterBoolean(self):
1597
    fielddefs = query._PrepareFieldList([
1598
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1599
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1600
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1601
       None, 0, lambda ctx, item: item["value"]),
1602
      ], [])
1603

    
1604
    data = [
1605
      { "name": "node1", "value": False, },
1606
      { "name": "node2", "value": True, },
1607
      { "name": "node3", "value": True, },
1608
      ]
1609

    
1610
    q = query.Query(fielddefs, ["name", "value"],
1611
                    qfilter=["|", ["=", "value", False],
1612
                                  ["=", "value", True]])
1613
    self.assertTrue(q.RequestedNames() is None)
1614
    self.assertEqual(q.Query(data), [
1615
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1616
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1617
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1618
      ])
1619

    
1620
    q = query.Query(fielddefs, ["name", "value"],
1621
                    qfilter=["|", ["=", "value", False],
1622
                                  ["!", ["=", "value", False]]])
1623
    self.assertTrue(q.RequestedNames() is None)
1624
    self.assertEqual(q.Query(data), [
1625
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1626
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1627
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1628
      ])
1629

    
1630
    # Comparing bool with string
1631
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1632
      self.assertRaises(errors.ParameterError, query.Query,
1633
                        fielddefs, ["name", "value"],
1634
                        qfilter=["=", "value", i])
1635

    
1636
    # Truth filter
1637
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1638
    self.assertTrue(q.RequestedNames() is None)
1639
    self.assertEqual(q.Query(data), [
1640
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1641
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1642
      ])
1643

    
1644
    # Negative bool filter
1645
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1646
    self.assertTrue(q.RequestedNames() is None)
1647
    self.assertEqual(q.Query(data), [
1648
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1649
      ])
1650

    
1651
    # Complex truth filter
1652
    q = query.Query(fielddefs, ["name", "value"],
1653
                    qfilter=["|", ["&", ["=", "name", "node1"],
1654
                                        ["!", ["?", "value"]]],
1655
                                  ["?", "value"]])
1656
    self.assertTrue(q.RequestedNames() is None)
1657
    self.assertEqual(q.Query(data), [
1658
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1659
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1660
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1661
      ])
1662

    
1663
  def testFilterRegex(self):
1664
    fielddefs = query._PrepareFieldList([
1665
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1666
       None, 0, lambda ctx, item: item["name"]),
1667
      ], [])
1668

    
1669
    data = [
1670
      { "name": "node1.example.com", },
1671
      { "name": "node2.site.example.com", },
1672
      { "name": "node2.example.net", },
1673

    
1674
      # Empty name
1675
      { "name": "", },
1676
      ]
1677

    
1678
    q = query.Query(fielddefs, ["name"], namefield="name",
1679
                    qfilter=["=~", "name", "site"])
1680
    self.assertTrue(q.RequestedNames() is None)
1681
    self.assertEqual(q.Query(data), [
1682
      [(constants.RS_NORMAL, "node2.site.example.com")],
1683
      ])
1684

    
1685
    q = query.Query(fielddefs, ["name"], namefield="name",
1686
                    qfilter=["=~", "name", "^node2"])
1687
    self.assertTrue(q.RequestedNames() is None)
1688
    self.assertEqual(q.Query(data), [
1689
      [(constants.RS_NORMAL, "node2.example.net")],
1690
      [(constants.RS_NORMAL, "node2.site.example.com")],
1691
      ])
1692

    
1693
    q = query.Query(fielddefs, ["name"], namefield="name",
1694
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1695
    self.assertTrue(q.RequestedNames() is None)
1696
    self.assertEqual(q.Query(data), [
1697
      [(constants.RS_NORMAL, "node1.example.com")],
1698
      [(constants.RS_NORMAL, "node2.site.example.com")],
1699
      ])
1700

    
1701
    q = query.Query(fielddefs, ["name"], namefield="name",
1702
                    qfilter=["=~", "name", r"."])
1703
    self.assertTrue(q.RequestedNames() is None)
1704
    self.assertEqual(q.Query(data), [
1705
      [(constants.RS_NORMAL, "node1.example.com")],
1706
      [(constants.RS_NORMAL, "node2.example.net")],
1707
      [(constants.RS_NORMAL, "node2.site.example.com")],
1708
      ])
1709

    
1710
    q = query.Query(fielddefs, ["name"], namefield="name",
1711
                    qfilter=["=~", "name", r"^$"])
1712
    self.assertTrue(q.RequestedNames() is None)
1713
    self.assertEqual(q.Query(data), [
1714
      [(constants.RS_NORMAL, "")],
1715
      ])
1716

    
1717
    # Invalid regular expression
1718
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1719
                      qfilter=["=~", "name", r"["])
1720

    
1721

    
1722
if __name__ == "__main__":
1723
  testutils.GanetiTestProgram()