Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 8930b0f0

History | View | Annotate | Download (66.5 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
                        ),
965
      objects.NodeGroup(name="restricted",
966
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
967
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
968
                        ipolicy=objects.MakeEmptyIPolicy(),
969
                        ndparams={}
970
                        ),
971
      ]
972
    self.cluster = objects.Cluster(cluster_name="testcluster",
973
      hvparams=constants.HVC_DEFAULTS,
974
      beparams={
975
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
976
        },
977
      nicparams={
978
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
979
        },
980
      ndparams=constants.NDC_DEFAULTS,
981
      ipolicy=constants.IPOLICY_DEFAULTS,
982
      )
983

    
984
  def _Create(self, selected):
985
    return query.Query(query.GROUP_FIELDS, selected)
986

    
987
  def testSimple(self):
988
    q = self._Create(["name", "uuid", "alloc_policy"])
989
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None)
990

    
991
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
992

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

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

    
1010
    q = self._Create(["name", "node_cnt", "node_list"])
1011
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None)
1012

    
1013
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1014

    
1015
    self.assertEqual(q.Query(gqd),
1016
                     [[(constants.RS_NORMAL, "default"),
1017
                       (constants.RS_NORMAL, 2),
1018
                       (constants.RS_NORMAL, ["node1", "node2"]),
1019
                       ],
1020
                      [(constants.RS_NORMAL, "restricted"),
1021
                       (constants.RS_NORMAL, 3),
1022
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1023
                       ],
1024
                      ])
1025

    
1026
  def testInstances(self):
1027
    groups_to_instances = {
1028
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1029
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1030
      }
1031

    
1032
    q = self._Create(["pinst_cnt", "pinst_list"])
1033
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1034
      groups_to_instances)
1035

    
1036
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1037

    
1038
    self.assertEqual(q.Query(gqd),
1039
                     [[(constants.RS_NORMAL, 2),
1040
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1041
                       ],
1042
                      [(constants.RS_NORMAL, 3),
1043
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1044
                       ],
1045
                      ])
1046

    
1047

    
1048
class TestOsQuery(unittest.TestCase):
1049
  def _Create(self, selected):
1050
    return query.Query(query.OS_FIELDS, selected)
1051

    
1052
  def test(self):
1053
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1054
    api_versions = [10, 0, 15, 5]
1055
    parameters = ["zpar3", "apar9"]
1056

    
1057
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1058
    assert (api_versions != sorted(api_versions) and
1059
            api_versions != utils.NiceSort(variants))
1060
    assert (parameters != sorted(parameters) and
1061
            parameters != utils.NiceSort(parameters))
1062

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

    
1074

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

    
1099

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

    
1111
  def testSomeFields(self):
1112
    rnd = random.Random(5357)
1113

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

    
1128

    
1129
class TestQueryFilter(unittest.TestCase):
1130
  def testRequestedNames(self):
1131
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1132

    
1133
    for fielddefs in query.ALL_FIELD_LISTS:
1134
      assert "name" in fielddefs
1135

    
1136
      # No name field
1137
      q = query.Query(fielddefs, ["name"], qfilter=["=", "name", "abc"],
1138
                      namefield=None)
1139
      self.assertEqual(q.RequestedNames(), None)
1140

    
1141
      # No filter
1142
      q = query.Query(fielddefs, ["name"], qfilter=None, namefield="name")
1143
      self.assertEqual(q.RequestedNames(), None)
1144

    
1145
      # Check empty query
1146
      q = query.Query(fielddefs, ["name"], qfilter=["|"], namefield="name")
1147
      self.assertEqual(q.RequestedNames(), None)
1148

    
1149
      # Check order
1150
      q = query.Query(fielddefs, ["name"], qfilter=["|"] + innerfilter,
1151
                      namefield="name")
1152
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1153

    
1154
      # Check reverse order
1155
      q = query.Query(fielddefs, ["name"],
1156
                      qfilter=["|"] + list(reversed(innerfilter)),
1157
                      namefield="name")
1158
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1159

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

    
1166
      # Unknown name field
1167
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1168
                        namefield="_unknown_field_")
1169

    
1170
      # Filter with AND
1171
      q = query.Query(fielddefs, ["name"],
1172
                      qfilter=["|", ["=", "name", "foo"],
1173
                                    ["&", ["=", "name", ""]]],
1174
                      namefield="name")
1175
      self.assertTrue(q.RequestedNames() is None)
1176

    
1177
      # Filter with NOT
1178
      q = query.Query(fielddefs, ["name"],
1179
                      qfilter=["|", ["=", "name", "foo"],
1180
                                    ["!", ["=", "name", ""]]],
1181
                      namefield="name")
1182
      self.assertTrue(q.RequestedNames() is None)
1183

    
1184
      # Filter with only OR (names must be in correct order)
1185
      q = query.Query(fielddefs, ["name"],
1186
                      qfilter=["|", ["=", "name", "x17361"],
1187
                                    ["|", ["=", "name", "x22015"]],
1188
                                    ["|", ["|", ["=", "name", "x13193"]]],
1189
                                    ["=", "name", "x15215"]],
1190
                      namefield="name")
1191
      self.assertEqual(q.RequestedNames(),
1192
                       ["x17361", "x22015", "x13193", "x15215"])
1193

    
1194
  @staticmethod
1195
  def _GenNestedFilter(op, depth):
1196
    nested = ["=", "name", "value"]
1197
    for i in range(depth):
1198
      nested = [op, nested]
1199
    return nested
1200

    
1201
  def testCompileFilter(self):
1202
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1203

    
1204
    checks = [
1205
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1206
      ["=", "_unknown_field", "value"],
1207
      self._GenNestedFilter("|", levels_max),
1208
      self._GenNestedFilter("|", levels_max * 3),
1209
      self._GenNestedFilter("!", levels_max),
1210
      ]
1211

    
1212
    for fielddefs in query.ALL_FIELD_LISTS:
1213
      for qfilter in checks:
1214
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1215
                          fielddefs, None, qfilter)
1216

    
1217
      for op in ["|", "!"]:
1218
        qfilter = self._GenNestedFilter(op, levels_max - 1)
1219
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1220
                                                      qfilter)))
1221

    
1222
  def testQueryInputOrder(self):
1223
    fielddefs = query._PrepareFieldList([
1224
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1225
       None, 0, lambda ctx, item: item["pnode"]),
1226
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1227
       None, 0, lambda ctx, item: item["snode"]),
1228
      ], [])
1229

    
1230
    data = [
1231
      { "pnode": "node1", "snode": "node44", },
1232
      { "pnode": "node30", "snode": "node90", },
1233
      { "pnode": "node25", "snode": "node1", },
1234
      { "pnode": "node20", "snode": "node1", },
1235
      ]
1236

    
1237
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1238

    
1239
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1240
                    qfilter=qfilter)
1241
    self.assertTrue(q.RequestedNames() is None)
1242
    self.assertFalse(q.RequestedData())
1243
    self.assertEqual(q.Query(data),
1244
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1245
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1246
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1247

    
1248
    # Try again with reversed input data
1249
    self.assertEqual(q.Query(reversed(data)),
1250
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1251
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1252
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1253

    
1254
    # No name field, result must be in incoming order
1255
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1256
                    qfilter=qfilter)
1257
    self.assertFalse(q.RequestedData())
1258
    self.assertEqual(q.Query(data),
1259
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1260
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1261
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1262
    self.assertEqual(q.OldStyleQuery(data), [
1263
      ["node1", "node44"],
1264
      ["node25", "node1"],
1265
      ["node20", "node1"],
1266
      ])
1267
    self.assertEqual(q.Query(reversed(data)),
1268
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1269
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1270
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1271
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1272
      ["node20", "node1"],
1273
      ["node25", "node1"],
1274
      ["node1", "node44"],
1275
      ])
1276

    
1277
    # Name field, but no sorting, result must be in incoming order
1278
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1279
    self.assertFalse(q.RequestedData())
1280
    self.assertEqual(q.Query(data, sort_by_name=False),
1281
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1282
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1283
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1284
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1285
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1286
      ["node1", "node44"],
1287
      ["node30", "node90"],
1288
      ["node25", "node1"],
1289
      ["node20", "node1"],
1290
      ])
1291
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1292
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1293
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1294
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1295
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1296
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1297
      ["node20", "node1"],
1298
      ["node25", "node1"],
1299
      ["node30", "node90"],
1300
      ["node1", "node44"],
1301
      ])
1302

    
1303
  def testEqualNamesOrder(self):
1304
    fielddefs = query._PrepareFieldList([
1305
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1306
       None, 0, lambda ctx, item: item["pnode"]),
1307
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1308
       None, 0, lambda ctx, item: item["num"]),
1309
      ], [])
1310

    
1311
    data = [
1312
      { "pnode": "node1", "num": 100, },
1313
      { "pnode": "node1", "num": 25, },
1314
      { "pnode": "node2", "num": 90, },
1315
      { "pnode": "node2", "num": 30, },
1316
      ]
1317

    
1318
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1319
                    qfilter=["|", ["=", "pnode", "node1"],
1320
                                  ["=", "pnode", "node2"],
1321
                                  ["=", "pnode", "node1"]])
1322
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1323
                     msg="Did not return unique names")
1324
    self.assertFalse(q.RequestedData())
1325
    self.assertEqual(q.Query(data),
1326
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1327
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1328
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1329
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1330
    self.assertEqual(q.Query(data, sort_by_name=False),
1331
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1332
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1333
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1334
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1335

    
1336
    data = [
1337
      { "pnode": "nodeX", "num": 50, },
1338
      { "pnode": "nodeY", "num": 40, },
1339
      { "pnode": "nodeX", "num": 30, },
1340
      { "pnode": "nodeX", "num": 20, },
1341
      { "pnode": "nodeM", "num": 10, },
1342
      ]
1343

    
1344
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1345
                    qfilter=["|", ["=", "pnode", "nodeX"],
1346
                                  ["=", "pnode", "nodeY"],
1347
                                  ["=", "pnode", "nodeY"],
1348
                                  ["=", "pnode", "nodeY"],
1349
                                  ["=", "pnode", "nodeM"]])
1350
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1351
                     msg="Did not return unique names")
1352
    self.assertFalse(q.RequestedData())
1353

    
1354
    # First sorted by name, then input order
1355
    self.assertEqual(q.Query(data, sort_by_name=True),
1356
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1357
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1358
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1359
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1360
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1361

    
1362
    # Input order
1363
    self.assertEqual(q.Query(data, sort_by_name=False),
1364
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1365
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1366
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1367
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1368
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1369

    
1370
  def testFilter(self):
1371
    (DK_A, DK_B) = range(1000, 1002)
1372

    
1373
    fielddefs = query._PrepareFieldList([
1374
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1375
       DK_A, 0, lambda ctx, item: item["name"]),
1376
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1377
       DK_B, 0, lambda ctx, item: item["other"]),
1378
      ], [])
1379

    
1380
    data = [
1381
      { "name": "node1", "other": "foo", },
1382
      { "name": "node2", "other": "bar", },
1383
      { "name": "node3", "other": "Hello", },
1384
      ]
1385

    
1386
    # Empty filter
1387
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1388
                    qfilter=["|"])
1389
    self.assertTrue(q.RequestedNames() is None)
1390
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1391
    self.assertEqual(q.Query(data), [])
1392

    
1393
    # Normal filter
1394
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1395
                    qfilter=["=", "name", "node1"])
1396
    self.assertEqual(q.RequestedNames(), ["node1"])
1397
    self.assertEqual(q.Query(data),
1398
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1399

    
1400
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1401
                    qfilter=(["|", ["=", "name", "node1"],
1402
                                   ["=", "name", "node3"]]))
1403
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1404
    self.assertEqual(q.Query(data),
1405
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1406
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1407

    
1408
    # Complex filter
1409
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1410
                    qfilter=(["|", ["=", "name", "node1"],
1411
                                   ["|", ["=", "name", "node3"],
1412
                                         ["=", "name", "node2"]],
1413
                                   ["=", "name", "node3"]]))
1414
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1415
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1416
    self.assertEqual(q.Query(data),
1417
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1418
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1419
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1420

    
1421
    # Filter data type mismatch
1422
    for i in [-1, 0, 1, 123, [], None, True, False]:
1423
      self.assertRaises(errors.ParameterError, query.Query,
1424
                        fielddefs, ["name", "other"], namefield="name",
1425
                        qfilter=["=", "name", i])
1426

    
1427
    # Negative filter
1428
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1429
                    qfilter=["!", ["|", ["=", "name", "node1"],
1430
                                        ["=", "name", "node3"]]])
1431
    self.assertTrue(q.RequestedNames() is None)
1432
    self.assertEqual(q.Query(data),
1433
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1434

    
1435
    # Not equal
1436
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1437
                    qfilter=["!=", "name", "node3"])
1438
    self.assertTrue(q.RequestedNames() is None)
1439
    self.assertEqual(q.Query(data),
1440
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1441
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1442

    
1443
    # Data type
1444
    q = query.Query(fielddefs, [], namefield="name",
1445
                    qfilter=["|", ["=", "other", "bar"],
1446
                                  ["=", "name", "foo"]])
1447
    self.assertTrue(q.RequestedNames() is None)
1448
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1449
    self.assertEqual(q.Query(data), [[]])
1450

    
1451
    # Only one data type
1452
    q = query.Query(fielddefs, ["other"], namefield="name",
1453
                    qfilter=["=", "other", "bar"])
1454
    self.assertTrue(q.RequestedNames() is None)
1455
    self.assertEqual(q.RequestedData(), set([DK_B]))
1456
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1457

    
1458
    q = query.Query(fielddefs, [], namefield="name",
1459
                    qfilter=["=", "other", "bar"])
1460
    self.assertTrue(q.RequestedNames() is None)
1461
    self.assertEqual(q.RequestedData(), set([DK_B]))
1462
    self.assertEqual(q.Query(data), [[]])
1463

    
1464
  def testFilterContains(self):
1465
    fielddefs = query._PrepareFieldList([
1466
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1467
       None, 0, lambda ctx, item: item["name"]),
1468
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1469
       None, 0, lambda ctx, item: item["other"]),
1470
      ], [])
1471

    
1472
    data = [
1473
      { "name": "node2", "other": ["x", "y", "bar"], },
1474
      { "name": "node3", "other": "Hello", },
1475
      { "name": "node1", "other": ["a", "b", "foo"], },
1476
      { "name": "empty", "other": []},
1477
      ]
1478

    
1479
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1480
                    qfilter=["=[]", "other", "bar"])
1481
    self.assertTrue(q.RequestedNames() is None)
1482
    self.assertEqual(q.Query(data), [
1483
      [(constants.RS_NORMAL, "node2"),
1484
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1485
      ])
1486

    
1487
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1488
                    qfilter=["|", ["=[]", "other", "bar"],
1489
                                  ["=[]", "other", "a"],
1490
                                  ["=[]", "other", "b"]])
1491
    self.assertTrue(q.RequestedNames() is None)
1492
    self.assertEqual(q.Query(data), [
1493
      [(constants.RS_NORMAL, "node1"),
1494
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1495
      [(constants.RS_NORMAL, "node2"),
1496
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1497
      ])
1498
    self.assertEqual(q.OldStyleQuery(data), [
1499
      ["node1", ["a", "b", "foo"]],
1500
      ["node2", ["x", "y", "bar"]],
1501
      ])
1502

    
1503
    # Boolean test
1504
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1505
                    qfilter=["?", "other"])
1506
    self.assertEqual(q.OldStyleQuery(data), [
1507
      ["node1", ["a", "b", "foo"]],
1508
      ["node2", ["x", "y", "bar"]],
1509
      ["node3", "Hello"],
1510
      ])
1511

    
1512
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1513
                    qfilter=["!", ["?", "other"]])
1514
    self.assertEqual(q.OldStyleQuery(data), [
1515
      ["empty", []],
1516
      ])
1517

    
1518
  def testFilterHostname(self):
1519
    fielddefs = query._PrepareFieldList([
1520
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1521
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1522
      ], [])
1523

    
1524
    data = [
1525
      { "name": "node1.example.com", },
1526
      { "name": "node2.example.com", },
1527
      { "name": "node2.example.net", },
1528
      ]
1529

    
1530
    q = query.Query(fielddefs, ["name"], namefield="name",
1531
                    qfilter=["=", "name", "node2"])
1532
    self.assertEqual(q.RequestedNames(), ["node2"])
1533
    self.assertEqual(q.Query(data), [
1534
      [(constants.RS_NORMAL, "node2.example.com")],
1535
      [(constants.RS_NORMAL, "node2.example.net")],
1536
      ])
1537

    
1538
    q = query.Query(fielddefs, ["name"], namefield="name",
1539
                    qfilter=["=", "name", "node1"])
1540
    self.assertEqual(q.RequestedNames(), ["node1"])
1541
    self.assertEqual(q.Query(data), [
1542
      [(constants.RS_NORMAL, "node1.example.com")],
1543
      ])
1544

    
1545
    q = query.Query(fielddefs, ["name"], namefield="name",
1546
                    qfilter=["=", "name", "othername"])
1547
    self.assertEqual(q.RequestedNames(), ["othername"])
1548
    self.assertEqual(q.Query(data), [])
1549

    
1550
    q = query.Query(fielddefs, ["name"], namefield="name",
1551
                    qfilter=["|", ["=", "name", "node1.example.com"],
1552
                                  ["=", "name", "node2"]])
1553
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1554
    self.assertEqual(q.Query(data), [
1555
      [(constants.RS_NORMAL, "node1.example.com")],
1556
      [(constants.RS_NORMAL, "node2.example.com")],
1557
      [(constants.RS_NORMAL, "node2.example.net")],
1558
      ])
1559
    self.assertEqual(q.OldStyleQuery(data), [
1560
      ["node1.example.com"],
1561
      ["node2.example.com"],
1562
      ["node2.example.net"],
1563
      ])
1564

    
1565
    q = query.Query(fielddefs, ["name"], namefield="name",
1566
                    qfilter=["!=", "name", "node1"])
1567
    self.assertTrue(q.RequestedNames() is None)
1568
    self.assertEqual(q.Query(data), [
1569
      [(constants.RS_NORMAL, "node2.example.com")],
1570
      [(constants.RS_NORMAL, "node2.example.net")],
1571
      ])
1572
    self.assertEqual(q.OldStyleQuery(data), [
1573
      ["node2.example.com"],
1574
      ["node2.example.net"],
1575
      ])
1576

    
1577
  def testFilterBoolean(self):
1578
    fielddefs = query._PrepareFieldList([
1579
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1580
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1581
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1582
       None, 0, lambda ctx, item: item["value"]),
1583
      ], [])
1584

    
1585
    data = [
1586
      { "name": "node1", "value": False, },
1587
      { "name": "node2", "value": True, },
1588
      { "name": "node3", "value": True, },
1589
      ]
1590

    
1591
    q = query.Query(fielddefs, ["name", "value"],
1592
                    qfilter=["|", ["=", "value", False],
1593
                                  ["=", "value", True]])
1594
    self.assertTrue(q.RequestedNames() is None)
1595
    self.assertEqual(q.Query(data), [
1596
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1597
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1598
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1599
      ])
1600

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

    
1611
    # Comparing bool with string
1612
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1613
      self.assertRaises(errors.ParameterError, query.Query,
1614
                        fielddefs, ["name", "value"],
1615
                        qfilter=["=", "value", i])
1616

    
1617
    # Truth filter
1618
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1619
    self.assertTrue(q.RequestedNames() is None)
1620
    self.assertEqual(q.Query(data), [
1621
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1622
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1623
      ])
1624

    
1625
    # Negative bool filter
1626
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1627
    self.assertTrue(q.RequestedNames() is None)
1628
    self.assertEqual(q.Query(data), [
1629
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1630
      ])
1631

    
1632
    # Complex truth filter
1633
    q = query.Query(fielddefs, ["name", "value"],
1634
                    qfilter=["|", ["&", ["=", "name", "node1"],
1635
                                        ["!", ["?", "value"]]],
1636
                                  ["?", "value"]])
1637
    self.assertTrue(q.RequestedNames() is None)
1638
    self.assertEqual(q.Query(data), [
1639
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1640
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1641
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1642
      ])
1643

    
1644
  def testFilterRegex(self):
1645
    fielddefs = query._PrepareFieldList([
1646
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1647
       None, 0, lambda ctx, item: item["name"]),
1648
      ], [])
1649

    
1650
    data = [
1651
      { "name": "node1.example.com", },
1652
      { "name": "node2.site.example.com", },
1653
      { "name": "node2.example.net", },
1654

    
1655
      # Empty name
1656
      { "name": "", },
1657
      ]
1658

    
1659
    q = query.Query(fielddefs, ["name"], namefield="name",
1660
                    qfilter=["=~", "name", "site"])
1661
    self.assertTrue(q.RequestedNames() is None)
1662
    self.assertEqual(q.Query(data), [
1663
      [(constants.RS_NORMAL, "node2.site.example.com")],
1664
      ])
1665

    
1666
    q = query.Query(fielddefs, ["name"], namefield="name",
1667
                    qfilter=["=~", "name", "^node2"])
1668
    self.assertTrue(q.RequestedNames() is None)
1669
    self.assertEqual(q.Query(data), [
1670
      [(constants.RS_NORMAL, "node2.example.net")],
1671
      [(constants.RS_NORMAL, "node2.site.example.com")],
1672
      ])
1673

    
1674
    q = query.Query(fielddefs, ["name"], namefield="name",
1675
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1676
    self.assertTrue(q.RequestedNames() is None)
1677
    self.assertEqual(q.Query(data), [
1678
      [(constants.RS_NORMAL, "node1.example.com")],
1679
      [(constants.RS_NORMAL, "node2.site.example.com")],
1680
      ])
1681

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

    
1691
    q = query.Query(fielddefs, ["name"], namefield="name",
1692
                    qfilter=["=~", "name", r"^$"])
1693
    self.assertTrue(q.RequestedNames() is None)
1694
    self.assertEqual(q.Query(data), [
1695
      [(constants.RS_NORMAL, "")],
1696
      ])
1697

    
1698
    # Invalid regular expression
1699
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1700
                      qfilter=["=~", "name", r"["])
1701

    
1702

    
1703
if __name__ == "__main__":
1704
  testutils.GanetiTestProgram()