Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.query_unittest.py @ 5b798711

History | View | Annotate | Download (75.6 kB)

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

    
4
# Copyright (C) 2010, 2011, 2012, 2013 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 ganeti.masterd.instance as gmi
37
from ganeti.hypervisor import hv_base
38

    
39
import testutils
40

    
41

    
42
class TestConstants(unittest.TestCase):
43
  def test(self):
44
    self.assertEqual(set(query._VERIFY_FN.keys()),
45
                     constants.QFT_ALL)
46

    
47

    
48
class _QueryData:
49
  def __init__(self, data, **kwargs):
50
    self.data = data
51

    
52
    for name, value in kwargs.items():
53
      setattr(self, name, value)
54

    
55
  def __iter__(self):
56
    return iter(self.data)
57

    
58

    
59
def _GetDiskSize(nr, ctx, item):
60
  disks = item["disks"]
61
  try:
62
    return disks[nr]
63
  except IndexError:
64
    return query._FS_UNAVAIL
65

    
66

    
67
class TestQuery(unittest.TestCase):
68
  def test(self):
69
    (STATIC, DISK) = range(10, 12)
70

    
71
    fielddef = query._PrepareFieldList([
72
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
73
       STATIC, 0, lambda ctx, item: item["name"]),
74
      (query._MakeField("master", "Master", constants.QFT_BOOL, "Master"),
75
       STATIC, 0, lambda ctx, item: ctx.mastername == item["name"]),
76
      ] +
77
      [(query._MakeField("disk%s.size" % i, "DiskSize%s" % i,
78
                         constants.QFT_UNIT, "Disk size %s" % i),
79
        DISK, 0, compat.partial(_GetDiskSize, i))
80
       for i in range(4)], [])
81

    
82
    q = query.Query(fielddef, ["name"])
83
    self.assertEqual(q.RequestedData(), set([STATIC]))
84
    self.assertEqual(len(q._fields), 1)
85
    self.assertEqual(len(q.GetFields()), 1)
86
    self.assertEqual(q.GetFields()[0].ToDict(),
87
      objects.QueryFieldDefinition(name="name",
88
                                   title="Name",
89
                                   kind=constants.QFT_TEXT,
90
                                   doc="Name").ToDict())
91

    
92
    # Create data only once query has been prepared
93
    data = [
94
      { "name": "node1", "disks": [0, 1, 2], },
95
      { "name": "node2", "disks": [3, 4], },
96
      { "name": "node3", "disks": [5, 6, 7], },
97
      ]
98

    
99
    self.assertEqual(q.Query(_QueryData(data, mastername="node3")),
100
                     [[(constants.RS_NORMAL, "node1")],
101
                      [(constants.RS_NORMAL, "node2")],
102
                      [(constants.RS_NORMAL, "node3")]])
103
    self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node3")),
104
                     [["node1"], ["node2"], ["node3"]])
105

    
106
    q = query.Query(fielddef, ["name", "master"])
107
    self.assertEqual(q.RequestedData(), set([STATIC]))
108
    self.assertEqual(len(q._fields), 2)
109
    self.assertEqual(q.Query(_QueryData(data, mastername="node3")),
110
                     [[(constants.RS_NORMAL, "node1"),
111
                       (constants.RS_NORMAL, False)],
112
                      [(constants.RS_NORMAL, "node2"),
113
                       (constants.RS_NORMAL, False)],
114
                      [(constants.RS_NORMAL, "node3"),
115
                       (constants.RS_NORMAL, True)],
116
                     ])
117

    
118
    q = query.Query(fielddef, ["name", "master", "disk0.size"])
119
    self.assertEqual(q.RequestedData(), set([STATIC, DISK]))
120
    self.assertEqual(len(q._fields), 3)
121
    self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
122
                     [[(constants.RS_NORMAL, "node1"),
123
                       (constants.RS_NORMAL, False),
124
                       (constants.RS_NORMAL, 0)],
125
                      [(constants.RS_NORMAL, "node2"),
126
                       (constants.RS_NORMAL, True),
127
                       (constants.RS_NORMAL, 3)],
128
                      [(constants.RS_NORMAL, "node3"),
129
                       (constants.RS_NORMAL, False),
130
                       (constants.RS_NORMAL, 5)],
131
                     ])
132

    
133
    # With unknown column
134
    q = query.Query(fielddef, ["disk2.size", "disk1.size", "disk99.size",
135
                               "disk0.size"])
136
    self.assertEqual(q.RequestedData(), set([DISK]))
137
    self.assertEqual(len(q._fields), 4)
138
    self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
139
                     [[(constants.RS_NORMAL, 2),
140
                       (constants.RS_NORMAL, 1),
141
                       (constants.RS_UNKNOWN, None),
142
                       (constants.RS_NORMAL, 0)],
143
                      [(constants.RS_UNAVAIL, None),
144
                       (constants.RS_NORMAL, 4),
145
                       (constants.RS_UNKNOWN, None),
146
                       (constants.RS_NORMAL, 3)],
147
                      [(constants.RS_NORMAL, 7),
148
                       (constants.RS_NORMAL, 6),
149
                       (constants.RS_UNKNOWN, None),
150
                       (constants.RS_NORMAL, 5)],
151
                     ])
152
    self.assertRaises(errors.OpPrereqError, q.OldStyleQuery,
153
                      _QueryData(data, mastername="node2"))
154
    self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], [
155
                     { "name": "disk2.size", "title": "DiskSize2",
156
                       "kind": constants.QFT_UNIT, "doc": "Disk size 2", },
157
                     { "name": "disk1.size", "title": "DiskSize1",
158
                       "kind": constants.QFT_UNIT, "doc": "Disk size 1", },
159
                     { "name": "disk99.size", "title": "disk99.size",
160
                       "kind": constants.QFT_UNKNOWN,
161
                       "doc": "Unknown field 'disk99.size'", },
162
                     { "name": "disk0.size", "title": "DiskSize0",
163
                       "kind": constants.QFT_UNIT, "doc": "Disk size 0", },
164
                     ])
165

    
166
    # Empty query
167
    q = query.Query(fielddef, [])
168
    self.assertEqual(q.RequestedData(), set([]))
169
    self.assertEqual(len(q._fields), 0)
170
    self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
171
                     [[], [], []])
172
    self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node2")),
173
                     [[], [], []])
174
    self.assertEqual(q.GetFields(), [])
175

    
176
  def testPrepareFieldList(self):
177
    # Duplicate titles
178
    for (a, b) in [("name", "name"), ("NAME", "name")]:
179
      self.assertRaises(AssertionError, query._PrepareFieldList, [
180
        (query._MakeField("name", b, constants.QFT_TEXT, "Name"), None, 0,
181
         lambda *args: None),
182
        (query._MakeField("other", a, constants.QFT_TEXT, "Other"), None, 0,
183
         lambda *args: None),
184
        ], [])
185

    
186
    # Non-lowercase names
187
    self.assertRaises(AssertionError, query._PrepareFieldList, [
188
      (query._MakeField("NAME", "Name", constants.QFT_TEXT, "Name"), None, 0,
189
       lambda *args: None),
190
      ], [])
191
    self.assertRaises(AssertionError, query._PrepareFieldList, [
192
      (query._MakeField("Name", "Name", constants.QFT_TEXT, "Name"), None, 0,
193
       lambda *args: None),
194
      ], [])
195

    
196
    # Empty name
197
    self.assertRaises(AssertionError, query._PrepareFieldList, [
198
      (query._MakeField("", "Name", constants.QFT_TEXT, "Name"), None, 0,
199
       lambda *args: None),
200
      ], [])
201

    
202
    # Empty title
203
    self.assertRaises(AssertionError, query._PrepareFieldList, [
204
      (query._MakeField("name", "", constants.QFT_TEXT, "Name"), None, 0,
205
       lambda *args: None),
206
      ], [])
207

    
208
    # Whitespace in title
209
    self.assertRaises(AssertionError, query._PrepareFieldList, [
210
      (query._MakeField("name", "Co lu mn", constants.QFT_TEXT, "Name"),
211
       None, 0, lambda *args: None),
212
      ], [])
213

    
214
    # No callable function
215
    self.assertRaises(AssertionError, query._PrepareFieldList, [
216
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
217
       None, 0, None),
218
      ], [])
219

    
220
    # Invalid documentation
221
    for doc in ["", ".", "Hello world\n", "Hello\nWo\nrld", "Hello World!",
222
                "HelloWorld.", "only lowercase", ",", " x y z .\t", "  "]:
223
      self.assertRaises(AssertionError, query._PrepareFieldList, [
224
        (query._MakeField("name", "Name", constants.QFT_TEXT, doc),
225
        None, 0, lambda *args: None),
226
        ], [])
227

    
228
    # Duplicate field name
229
    self.assertRaises(ValueError, query._PrepareFieldList, [
230
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
231
       None, 0, lambda *args: None),
232
      (query._MakeField("name", "Other", constants.QFT_OTHER, "Other"),
233
       None, 0, lambda *args: None),
234
      ], [])
235

    
236
  def testUnknown(self):
237
    fielddef = query._PrepareFieldList([
238
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
239
       None, 0, lambda _, item: "name%s" % item),
240
      (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP, "Other"),
241
       None, 0, lambda *args: 1234),
242
      (query._MakeField("nodata", "NoData", constants.QFT_NUMBER, "No data"),
243
       None, 0, lambda *args: query._FS_NODATA ),
244
      (query._MakeField("unavail", "Unavail", constants.QFT_BOOL, "Unavail"),
245
       None, 0, lambda *args: query._FS_UNAVAIL),
246
      ], [])
247

    
248
    for selected in [["foo"], ["Hello", "World"],
249
                     ["name1", "other", "foo"]]:
250
      q = query.Query(fielddef, selected)
251
      self.assertEqual(len(q._fields), len(selected))
252
      self.assert_(compat.all(len(row) == len(selected)
253
                              for row in q.Query(_QueryData(range(1, 10)))))
254
      self.assertEqual(q.Query(_QueryData(range(1, 10))),
255
                       [[(constants.RS_UNKNOWN, None)] * len(selected)
256
                        for i in range(1, 10)])
257
      self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
258
                       [{ "name": name, "title": name,
259
                          "kind": constants.QFT_UNKNOWN,
260
                          "doc": "Unknown field '%s'" % name}
261
                        for name in selected])
262

    
263
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
264
    self.assertEqual(len(q._fields), 4)
265
    self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
266
                     ["name%s" % i, 1234, None, None]
267
                     for i in range(1, 10)
268
                     ])
269

    
270
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
271
    self.assertEqual(len(q._fields), 5)
272
    self.assertEqual(q.Query(_QueryData(range(1, 10))),
273
                     [[(constants.RS_NORMAL, "name%s" % i),
274
                       (constants.RS_NORMAL, 1234),
275
                       (constants.RS_NODATA, None),
276
                       (constants.RS_UNAVAIL, None),
277
                       (constants.RS_UNKNOWN, None)]
278
                      for i in range(1, 10)])
279

    
280
  def testAliases(self):
281
    fields = [
282
      (query._MakeField("a", "a-title", constants.QFT_TEXT, "Field A"),
283
       None, 0, lambda *args: None),
284
      (query._MakeField("b", "b-title", constants.QFT_TEXT, "Field B"),
285
       None, 0, lambda *args: None),
286
      ]
287
    # duplicate field
288
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
289
                      [("b", "a")])
290
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
291
                      [("c", "b"), ("c", "a")])
292
    # missing target
293
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
294
                      [("c", "d")])
295
    fdefs = query._PrepareFieldList(fields, [("c", "b")])
296
    self.assertEqual(len(fdefs), 3)
297
    self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
298

    
299

    
300
class TestGetNodeRole(unittest.TestCase):
301
  def test(self):
302
    tested_role = set()
303
    master_uuid = "969502b9-f632-4d3d-83a5-a78b0ca8cdf6"
304
    node_uuid = "d75499b5-83e3-4b80-b6fe-3e1aee7e5a35"
305
    checks = [
306
      (constants.NR_MASTER,
307
       objects.Node(name="node1", uuid=master_uuid)),
308
      (constants.NR_MCANDIDATE,
309
       objects.Node(name="node1", uuid=node_uuid, master_candidate=True)),
310
      (constants.NR_REGULAR,
311
       objects.Node(name="node1", uuid=node_uuid)),
312
      (constants.NR_DRAINED,
313
       objects.Node(name="node1", uuid=node_uuid, drained=True)),
314
      (constants.NR_OFFLINE,
315
       objects.Node(name="node1", uuid=node_uuid, offline=True)),
316
      ]
317

    
318
    for (role, node) in checks:
319
      result = query._GetNodeRole(node, master_uuid)
320
      self.assertEqual(result, role)
321
      tested_role.add(result)
322

    
323
    self.assertEqual(tested_role, constants.NR_ALL)
324

    
325

    
326
class TestNodeQuery(unittest.TestCase):
327
  def _Create(self, selected):
328
    return query.Query(query.NODE_FIELDS, selected)
329

    
330
  def testSimple(self):
331
    cluster = objects.Cluster(cluster_name="testcluster",
332
                              ndparams=constants.NDC_DEFAULTS.copy())
333
    grp1 = objects.NodeGroup(name="default",
334
                             uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
335
                             alloc_policy=constants.ALLOC_POLICY_PREFERRED,
336
                             ipolicy=objects.MakeEmptyIPolicy(),
337
                             ndparams={},
338
                             )
339
    grp2 = objects.NodeGroup(name="group2",
340
                             uuid="c0e89160-18e7-11e0-a46e-001d0904babe",
341
                             alloc_policy=constants.ALLOC_POLICY_PREFERRED,
342
                             ipolicy=objects.MakeEmptyIPolicy(),
343
                             ndparams={constants.ND_SPINDLE_COUNT: 2},
344
                             )
345
    groups = {grp1.uuid: grp1, grp2.uuid: grp2}
346
    nodes = [
347
      objects.Node(name="node1", drained=False, group=grp1.uuid, ndparams={}),
348
      objects.Node(name="node2", drained=True, group=grp2.uuid, ndparams={}),
349
      objects.Node(name="node3", drained=False, group=grp1.uuid,
350
                   ndparams={constants.ND_SPINDLE_COUNT: 4}),
351
      ]
352
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
353
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None,
354
                                groups, None, cluster)
355

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

    
381
  def test(self):
382
    selected = query.NODE_FIELDS.keys()
383
    field_index = dict((field, idx) for idx, field in enumerate(selected))
384

    
385
    q = self._Create(selected)
386
    self.assertEqual(q.RequestedData(),
387
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
388
                          query.NQ_GROUP, query.NQ_OOB]))
389

    
390
    cluster = objects.Cluster(cluster_name="testcluster",
391
      hvparams=constants.HVC_DEFAULTS,
392
      beparams={
393
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
394
        },
395
      nicparams={
396
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
397
        },
398
      ndparams=constants.NDC_DEFAULTS,
399
        )
400

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

    
422
    master_node = nodes[3]
423
    master_node.AddTag("masternode")
424
    master_node.AddTag("another")
425
    master_node.AddTag("tag")
426
    master_node.ctime = None
427
    master_node.mtime = None
428
    assert master_node.name == master_name
429

    
430
    live_data_node = nodes[4]
431
    assert live_data_node.name != master_name
432

    
433
    fake_live_data = {
434
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
435
      "cnodes": 4,
436
      "cnos": 3,
437
      "csockets": 4,
438
      "ctotal": 8,
439
      "mnode": 128,
440
      "mfree": 100,
441
      "mtotal": 4096,
442
      "dfree": 5 * 1024 * 1024,
443
      "dtotal": 100 * 1024 * 1024,
444
      "spfree": 0,
445
      "sptotal": 0,
446
      }
447

    
448
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
449
            sorted(fake_live_data.keys()))
450

    
451
    live_data = dict.fromkeys([node.uuid for node in nodes], {})
452
    live_data[live_data_node.uuid] = \
453
      dict((query._NODE_LIVE_FIELDS[name][2], value)
454
           for name, value in fake_live_data.items())
455

    
456
    node_to_primary_uuid = dict((node.uuid, set()) for node in nodes)
457
    node_to_primary_uuid[master_node.uuid].update(["inst1", "inst2"])
458

    
459
    node_to_secondary_uuid = dict((node.uuid, set()) for node in nodes)
460
    node_to_secondary_uuid[live_data_node.uuid].update(["instX", "instY",
461
                                                        "instZ"])
462

    
463
    inst_uuid_to_inst_name = {
464
      "inst1": "inst1-name",
465
      "inst2": "inst2-name",
466
      "instX": "instX-name",
467
      "instY": "instY-name",
468
      "instZ": "instZ-name"
469
    }
470

    
471
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
472
    groups = {
473
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
474
      }
475

    
476
    oob_not_powered_node = nodes[0]
477
    oob_not_powered_node.powered = False
478
    oob_support = dict((node.uuid, False) for node in nodes)
479
    oob_support[master_node.uuid] = True
480
    oob_support[oob_not_powered_node.uuid] = True
481

    
482
    master_node.group = ng_uuid
483

    
484
    nqd = query.NodeQueryData(nodes, live_data, master_node.uuid,
485
                              node_to_primary_uuid, node_to_secondary_uuid,
486
                              inst_uuid_to_inst_name, groups, oob_support,
487
                              cluster)
488
    result = q.Query(nqd)
489
    self.assert_(compat.all(len(row) == len(selected) for row in result))
490
    self.assertEqual([row[field_index["name"]] for row in result],
491
                     [(constants.RS_NORMAL, name) for name in node_names])
492

    
493
    node_to_row = dict((row[field_index["name"]][1], idx)
494
                       for idx, row in enumerate(result))
495

    
496
    master_row = result[node_to_row[master_name]]
497
    self.assert_(master_row[field_index["master"]])
498
    self.assert_(master_row[field_index["role"]], "M")
499
    self.assertEqual(master_row[field_index["group"]],
500
                     (constants.RS_NORMAL, "ng1"))
501
    self.assertEqual(master_row[field_index["group.uuid"]],
502
                     (constants.RS_NORMAL, ng_uuid))
503
    self.assertEqual(master_row[field_index["ctime"]],
504
                     (constants.RS_UNAVAIL, None))
505
    self.assertEqual(master_row[field_index["mtime"]],
506
                     (constants.RS_UNAVAIL, None))
507

    
508
    self.assert_(row[field_index["pip"]] == node.primary_ip and
509
                 row[field_index["sip"]] == node.secondary_ip and
510
                 set(row[field_index["tags"]]) == node.GetTags() and
511
                 row[field_index["serial_no"]] == node.serial_no and
512
                 row[field_index["role"]] == query._GetNodeRole(node,
513
                                                                master_name) and
514
                 (node.name == master_name or
515
                  (row[field_index["group"]] == "<unknown>" and
516
                   row[field_index["group.uuid"]] is None and
517
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
518
                                                 node.ctime) and
519
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
520
                                                 node.mtime) and
521
                   row[field_index["powered"]] == (constants.RS_NORMAL,
522
                                                   True))) or
523
                 (node.name == oob_not_powered_node and
524
                  row[field_index["powered"]] == (constants.RS_NORMAL,
525
                                                  False)) or
526
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
527
                 for row, node in zip(result, nodes))
528

    
529
    live_data_row = result[node_to_row[live_data_node.name]]
530

    
531
    for (field, value) in fake_live_data.items():
532
      self.assertEqual(live_data_row[field_index[field]],
533
                       (constants.RS_NORMAL, value))
534

    
535
    self.assertEqual(master_row[field_index["pinst_cnt"]],
536
                     (constants.RS_NORMAL, 2))
537
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
538
                     (constants.RS_NORMAL, 3))
539
    self.assertEqual(master_row[field_index["pinst_list"]],
540
                     (constants.RS_NORMAL,
541
                      [inst_uuid_to_inst_name[uuid] for uuid in
542
                       node_to_primary_uuid[master_node.uuid]]))
543
    self.assertEqual(live_data_row[field_index["sinst_list"]],
544
                     (constants.RS_NORMAL,
545
                      utils.NiceSort(
546
                        [inst_uuid_to_inst_name[uuid] for uuid in
547
                        node_to_secondary_uuid[live_data_node.uuid]])))
548

    
549
  def testGetLiveNodeField(self):
550
    nodes = [
551
      objects.Node(name="node1", drained=False, offline=False,
552
                   vm_capable=True),
553
      objects.Node(name="node2", drained=True, offline=False,
554
                   vm_capable=True),
555
      objects.Node(name="node3", drained=False, offline=False,
556
                   vm_capable=True),
557
      objects.Node(name="node4", drained=False, offline=True,
558
                   vm_capable=True),
559
      objects.Node(name="node5", drained=False, offline=False,
560
                   vm_capable=False),
561
      ]
562
    live_data = dict.fromkeys([node.name for node in nodes], {})
563

    
564
    # No data
565
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None,
566
                              None)
567
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
568
                                             nqd, nodes[0]),
569
                     query._FS_NODATA)
570

    
571
    # Missing field
572
    ctx = _QueryData(None, curlive_data={
573
      "some": 1,
574
      "other": 2,
575
      })
576
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
577
                                             ctx, nodes[0]),
578
                     query._FS_UNAVAIL)
579

    
580
    # Wrong format/datatype
581
    ctx = _QueryData(None, curlive_data={
582
      "hello": ["Hello World"],
583
      "other": 2,
584
      })
585
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
586
                                             ctx, nodes[0]),
587
                     query._FS_UNAVAIL)
588

    
589
    # Offline node
590
    assert nodes[3].offline
591
    ctx = _QueryData(None, curlive_data={})
592
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
593
                                             ctx, nodes[3]),
594
                     query._FS_OFFLINE, None)
595

    
596
    # Wrong field type
597
    ctx = _QueryData(None, curlive_data={"hello": 123})
598
    self.assertRaises(AssertionError, query._GetLiveNodeField,
599
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
600

    
601
    # Non-vm_capable node
602
    assert not nodes[4].vm_capable
603
    ctx = _QueryData(None, curlive_data={})
604
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
605
                                             ctx, nodes[4]),
606
                     query._FS_UNAVAIL, None)
607

    
608

    
609
class TestInstanceQuery(unittest.TestCase):
610
  def _Create(self, selected):
611
    return query.Query(query.INSTANCE_FIELDS, selected)
612

    
613
  def testSimple(self):
614
    q = self._Create(["name", "be/maxmem", "ip"])
615
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
616

    
617
    cluster = objects.Cluster(cluster_name="testcluster",
618
      hvparams=constants.HVC_DEFAULTS,
619
      beparams={
620
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
621
        },
622
      nicparams={
623
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
624
        },
625
      os_hvp={},
626
      osparams={})
627

    
628
    instances = [
629
      objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
630
                       nics=[], os="deb1"),
631
      objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
632
        os="foomoo",
633
        beparams={
634
          constants.BE_MAXMEM: 512,
635
        }),
636
      objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
637
        os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
638
      ]
639

    
640
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
641
                                  set(), {}, None, None, None)
642
    self.assertEqual(q.Query(iqd),
643
      [[(constants.RS_NORMAL, "inst1"),
644
        (constants.RS_NORMAL, 128),
645
        (constants.RS_UNAVAIL, None),
646
       ],
647
       [(constants.RS_NORMAL, "inst2"),
648
        (constants.RS_NORMAL, 512),
649
        (constants.RS_UNAVAIL, None),
650
       ],
651
       [(constants.RS_NORMAL, "inst3"),
652
        (constants.RS_NORMAL, 128),
653
        (constants.RS_NORMAL, "192.0.2.99"),
654
       ]])
655
    self.assertEqual(q.OldStyleQuery(iqd),
656
      [["inst1", 128, None],
657
       ["inst2", 512, None],
658
       ["inst3", 128, "192.0.2.99"]])
659

    
660
  def test(self):
661
    selected = query.INSTANCE_FIELDS.keys()
662
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
663

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

    
667
    q = self._Create(selected)
668
    self.assertEqual(q.RequestedData(),
669
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
670
                          query.IQ_CONSOLE, query.IQ_NODES, query.IQ_NETWORKS]))
671

    
672
    cluster = objects.Cluster(cluster_name="testcluster",
673
      hvparams=constants.HVC_DEFAULTS,
674
      beparams={
675
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
676
        },
677
      nicparams={
678
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
679
        },
680
      os_hvp={},
681
      tcpudp_port_pool=set(),
682
      osparams={
683
        "deb99": {
684
          "clean_install": "yes",
685
          },
686
        })
687

    
688
    offline_nodes = ["nodeoff1-uuid", "nodeoff2-uuid"]
689
    bad_nodes = ["nodebad1-uuid", "nodebad2-uuid", "nodebad3-uuid"] +\
690
                offline_nodes
691
    node_uuids = ["node%s-uuid" % i for i in range(10)] + bad_nodes
692

    
693
    instances = [
694
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
695
        uuid="inst1-uuid",
696
        ctime=1291244000, mtime=1291244400, serial_no=30,
697
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_PVM,
698
        os="linux1",
699
        primary_node="node1-uuid",
700
        disk_template=constants.DT_PLAIN,
701
        disks=[],
702
        disks_active=True,
703
        osparams={}),
704
      objects.Instance(name="inst2", hvparams={}, nics=[],
705
        uuid="inst2-uuid",
706
        ctime=1291211000, mtime=1291211077, serial_no=1,
707
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
708
        os="deb99",
709
        primary_node="node5-uuid",
710
        disk_template=constants.DT_DISKLESS,
711
        disks=[],
712
        disks_active=True,
713
        beparams={
714
          constants.BE_MAXMEM: 512,
715
          constants.BE_MINMEM: 256,
716
        },
717
        osparams={}),
718
      objects.Instance(name="inst3", hvparams={}, beparams={},
719
        uuid="inst3-uuid",
720
        ctime=1291011000, mtime=1291013000, serial_no=1923,
721
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_KVM,
722
        os="busybox",
723
        primary_node="node6-uuid",
724
        disk_template=constants.DT_DRBD8,
725
        disks=[],
726
        disks_active=False,
727
        nics=[
728
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
729
                      nicparams={
730
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
731
                        }),
732
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
733
          ],
734
        osparams={}),
735
      objects.Instance(name="inst4", hvparams={}, beparams={},
736
        uuid="inst4-uuid",
737
        ctime=1291244390, mtime=1291244395, serial_no=25,
738
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_PVM,
739
        os="linux1",
740
        primary_node="nodeoff2-uuid",
741
        disk_template=constants.DT_DRBD8,
742
        disks=[],
743
        disks_active=True,
744
        nics=[
745
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
746
                      nicparams={
747
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
748
                        }),
749
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
750
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
751
                      nicparams={
752
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
753
                        }),
754
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
755
                      nicparams={
756
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
757
                        constants.NIC_LINK: "eth123",
758
                        }),
759
          ],
760
        osparams={}),
761
      objects.Instance(name="inst5", hvparams={}, nics=[],
762
        uuid="inst5-uuid",
763
        ctime=1231211000, mtime=1261200000, serial_no=3,
764
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
765
        os="deb99",
766
        primary_node="nodebad2-uuid",
767
        disk_template=constants.DT_DISKLESS,
768
        disks=[],
769
        disks_active=True,
770
        beparams={
771
          constants.BE_MAXMEM: 512,
772
          constants.BE_MINMEM: 512,
773
        },
774
        osparams={}),
775
      objects.Instance(name="inst6", hvparams={}, nics=[],
776
        uuid="inst6-uuid",
777
        ctime=7513, mtime=11501, serial_no=13390,
778
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
779
        os="deb99",
780
        primary_node="node7-uuid",
781
        disk_template=constants.DT_DISKLESS,
782
        disks=[],
783
        disks_active=False,
784
        beparams={
785
          constants.BE_MAXMEM: 768,
786
          constants.BE_MINMEM: 256,
787
        },
788
        osparams={
789
          "clean_install": "no",
790
          }),
791
      objects.Instance(name="inst7", hvparams={}, nics=[],
792
        uuid="inst7-uuid",
793
        ctime=None, mtime=None, serial_no=1947,
794
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
795
        os="deb99",
796
        primary_node="node6-uuid",
797
        disk_template=constants.DT_DISKLESS,
798
        disks=[],
799
        disks_active=False,
800
        beparams={},
801
        osparams={}),
802
      objects.Instance(name="inst8", hvparams={}, nics=[],
803
        uuid="inst8-uuid",
804
        ctime=None, mtime=None, serial_no=19478,
805
        admin_state=constants.ADMINST_OFFLINE, hypervisor=constants.HT_XEN_HVM,
806
        os="deb99",
807
        primary_node="node6-uuid",
808
        disk_template=constants.DT_DISKLESS,
809
        disks=[],
810
        disks_active=False,
811
        beparams={},
812
        osparams={}),
813
      objects.Instance(name="inst9", hvparams={}, nics=[],
814
        uuid="inst9-uuid",
815
        ctime=None, mtime=None, serial_no=19478,
816
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
817
        os="deb99",
818
        primary_node="node6-uuid",
819
        disk_template=constants.DT_DISKLESS,
820
        disks=[],
821
        disks_active=False,
822
        beparams={},
823
        osparams={}),
824
      ]
825

    
826
    assert not utils.FindDuplicates(inst.uuid for inst in instances)
827
    assert not utils.FindDuplicates(inst.name for inst in instances)
828

    
829
    instbyname = dict((inst.name, inst) for inst in instances)
830

    
831
    disk_usage = dict((inst.uuid,
832
                       gmi.ComputeDiskSize(inst.disk_template,
833
                                           [{"size": disk.size}
834
                                           for disk in inst.disks]))
835
                      for inst in instances)
836

    
837
    inst_bridges = {
838
      "inst3-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
839
      "inst4-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
840
                     None, "eth123"],
841
      }
842

    
843
    live_data = {
844
      "inst2-uuid": {
845
        "vcpus": 3,
846
        "state": hv_base.HvInstanceState.RUNNING,
847
        },
848
      "inst4-uuid": {
849
        "memory": 123,
850
        "state": hv_base.HvInstanceState.RUNNING,
851
        },
852
      "inst6-uuid": {
853
        "memory": 768,
854
        "state": hv_base.HvInstanceState.RUNNING,
855
        },
856
      "inst7-uuid": {
857
        "vcpus": 3,
858
        "state": hv_base.HvInstanceState.RUNNING,
859
        },
860
      "inst9-uuid": {
861
        "vcpus": 3,
862
        "state": hv_base.HvInstanceState.SHUTDOWN,
863
        },
864
      }
865
    wrongnode_inst = set(["inst7-uuid"])
866

    
867
    consinfo = dict((inst.uuid, None) for inst in instances)
868
    consinfo["inst7-uuid"] = \
869
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
870
                              host=instbyname["inst7"].primary_node,
871
                              user="root",
872
                              command=["hostname"]).ToDict()
873

    
874
    nodes = dict([(uuid, objects.Node(
875
                           name="%s.example.com" % uuid,
876
                           uuid=uuid,
877
                           group="default-uuid"))
878
                  for uuid in node_uuids])
879

    
880
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
881
                                  offline_nodes, bad_nodes, live_data,
882
                                  wrongnode_inst, consinfo, nodes, {}, {})
883
    result = q.Query(iqd)
884
    self.assertEqual(len(result), len(instances))
885
    self.assert_(compat.all(len(row) == len(selected)
886
                            for row in result))
887

    
888
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
889
           "Offline nodes not included in bad nodes"
890

    
891
    tested_status = set()
892

    
893
    for (inst, row) in zip(instances, result):
894
      assert inst.primary_node in node_uuids
895

    
896
      self.assertEqual(row[fieldidx["name"]],
897
                       (constants.RS_NORMAL, inst.name))
898

    
899
      if inst.primary_node in offline_nodes:
900
        exp_status = constants.INSTST_NODEOFFLINE
901
      elif inst.primary_node in bad_nodes:
902
        exp_status = constants.INSTST_NODEDOWN
903
      elif inst.uuid in live_data:
904
        if inst.uuid in wrongnode_inst:
905
          exp_status = constants.INSTST_WRONGNODE
906
        else:
907
          instance_state = live_data[inst.uuid]["state"]
908
          if hv_base.HvInstanceState.IsShutdown(instance_state):
909
            if inst.admin_state == constants.ADMINST_UP:
910
              exp_status = constants.INSTST_USERDOWN
911
            else:
912
              exp_status = constants.INSTST_ADMINDOWN
913
          else:
914
            if inst.admin_state == constants.ADMINST_UP:
915
              exp_status = constants.INSTST_RUNNING
916
            else:
917
              exp_status = constants.INSTST_ERRORUP
918
      else:
919
        if inst.admin_state == constants.ADMINST_UP:
920
          exp_status = constants.INSTST_ERRORDOWN
921
        elif inst.admin_state == constants.ADMINST_DOWN:
922
          exp_status = constants.INSTST_ADMINDOWN
923
        else:
924
          exp_status = constants.INSTST_ADMINOFFLINE
925

    
926
      self.assertEqual(row[fieldidx["status"]],
927
                       (constants.RS_NORMAL, exp_status))
928

    
929
      (_, status) = row[fieldidx["status"]]
930
      tested_status.add(status)
931

    
932
      #FIXME(dynmem): check oper_ram vs min/max mem
933
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
934
        if inst.primary_node in bad_nodes:
935
          exp = (constants.RS_NODATA, None)
936
        elif inst.uuid in live_data:
937
          value = live_data[inst.uuid].get(livefield, None)
938
          if value is None:
939
            exp = (constants.RS_UNAVAIL, None)
940
          else:
941
            exp = (constants.RS_NORMAL, value)
942
        else:
943
          exp = (constants.RS_UNAVAIL, None)
944

    
945
        self.assertEqual(row[fieldidx[field]], exp)
946

    
947
      bridges = inst_bridges.get(inst.uuid, [])
948
      self.assertEqual(row[fieldidx["nic.bridges"]],
949
                       (constants.RS_NORMAL, bridges))
950
      if bridges:
951
        self.assertEqual(row[fieldidx["bridge"]],
952
                         (constants.RS_NORMAL, bridges[0]))
953
      else:
954
        self.assertEqual(row[fieldidx["bridge"]],
955
                         (constants.RS_UNAVAIL, None))
956

    
957
      for i in range(constants.MAX_NICS):
958
        if i < len(bridges) and bridges[i] is not None:
959
          exp = (constants.RS_NORMAL, bridges[i])
960
        else:
961
          exp = (constants.RS_UNAVAIL, None)
962
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
963

    
964
      if inst.primary_node in bad_nodes:
965
        exp = (constants.RS_NODATA, None)
966
      else:
967
        exp = (constants.RS_NORMAL, inst.uuid in live_data)
968
      self.assertEqual(row[fieldidx["oper_state"]], exp)
969

    
970
      cust_exp = (constants.RS_NORMAL, {})
971
      if inst.os == "deb99":
972
        if inst.uuid == "inst6-uuid":
973
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
974
          cust_exp = exp
975
        else:
976
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
977
      else:
978
        exp = (constants.RS_NORMAL, {})
979
      self.assertEqual(row[fieldidx["osparams"]], exp)
980
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
981

    
982
      usage = disk_usage[inst.uuid]
983
      if usage is None:
984
        usage = 0
985
      self.assertEqual(row[fieldidx["disk_usage"]],
986
                       (constants.RS_NORMAL, usage))
987

    
988
      for alias, target in [("sda_size", "disk.size/0"),
989
                            ("sdb_size", "disk.size/1"),
990
                            ("vcpus", "be/vcpus"),
991
                            ("ip", "nic.ip/0"),
992
                            ("mac", "nic.mac/0"),
993
                            ("bridge", "nic.bridge/0"),
994
                            ("nic_mode", "nic.mode/0"),
995
                            ("nic_link", "nic.link/0"),
996
                            ]:
997
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
998

    
999
      for field in ["ctime", "mtime"]:
1000
        if getattr(inst, field) is None:
1001
          # No ctime/mtime
1002
          exp = (constants.RS_UNAVAIL, None)
1003
        else:
1004
          exp = (constants.RS_NORMAL, getattr(inst, field))
1005
        self.assertEqual(row[fieldidx[field]], exp)
1006

    
1007
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
1008

    
1009
    # Ensure all possible status' have been tested
1010
    self.assertEqual(tested_status, set(constants.INSTST_ALL))
1011

    
1012
  def _CheckInstanceConsole(self, instance, (status, consdata)):
1013
    if instance.name == "inst7":
1014
      self.assertEqual(status, constants.RS_NORMAL)
1015
      console = objects.InstanceConsole.FromDict(consdata)
1016
      self.assertTrue(console.Validate())
1017
      self.assertEqual(console.host, instance.primary_node)
1018
    else:
1019
      self.assertEqual(status, constants.RS_UNAVAIL)
1020

    
1021

    
1022
class TestGroupQuery(unittest.TestCase):
1023

    
1024
  def setUp(self):
1025
    self.custom_diskparams = {
1026
      constants.DT_DRBD8: {
1027
        constants.DRBD_DEFAULT_METAVG: "foobar",
1028
      },
1029
    }
1030

    
1031
    self.groups = [
1032
      objects.NodeGroup(name="default",
1033
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
1034
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED,
1035
                        ipolicy=objects.MakeEmptyIPolicy(),
1036
                        ndparams={},
1037
                        diskparams={},
1038
                        ),
1039
      objects.NodeGroup(name="restricted",
1040
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
1041
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
1042
                        ipolicy=objects.MakeEmptyIPolicy(),
1043
                        ndparams={},
1044
                        diskparams=self.custom_diskparams,
1045
                        ),
1046
      ]
1047
    self.cluster = objects.Cluster(cluster_name="testcluster",
1048
      hvparams=constants.HVC_DEFAULTS,
1049
      beparams={
1050
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
1051
        },
1052
      nicparams={
1053
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
1054
        },
1055
      ndparams=constants.NDC_DEFAULTS,
1056
      ipolicy=constants.IPOLICY_DEFAULTS,
1057
      diskparams=constants.DISK_DT_DEFAULTS,
1058
      )
1059

    
1060
  def _Create(self, selected):
1061
    return query.Query(query.GROUP_FIELDS, selected)
1062

    
1063
  def testSimple(self):
1064
    q = self._Create(["name", "uuid", "alloc_policy"])
1065
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1066

    
1067
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1068

    
1069
    self.assertEqual(q.Query(gqd),
1070
      [[(constants.RS_NORMAL, "default"),
1071
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1072
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1073
        ],
1074
       [(constants.RS_NORMAL, "restricted"),
1075
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1076
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1077
        ],
1078
       ])
1079

    
1080
  def testNodes(self):
1081
    groups_to_nodes = {
1082
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1083
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1084
      }
1085

    
1086
    q = self._Create(["name", "node_cnt", "node_list"])
1087
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1088
                               False)
1089

    
1090
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1091

    
1092
    self.assertEqual(q.Query(gqd),
1093
                     [[(constants.RS_NORMAL, "default"),
1094
                       (constants.RS_NORMAL, 2),
1095
                       (constants.RS_NORMAL, ["node1", "node2"]),
1096
                       ],
1097
                      [(constants.RS_NORMAL, "restricted"),
1098
                       (constants.RS_NORMAL, 3),
1099
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1100
                       ],
1101
                      ])
1102

    
1103
  def testInstances(self):
1104
    groups_to_instances = {
1105
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1106
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1107
      }
1108

    
1109
    q = self._Create(["pinst_cnt", "pinst_list"])
1110
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1111
      groups_to_instances, False)
1112

    
1113
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1114

    
1115
    self.assertEqual(q.Query(gqd),
1116
                     [[(constants.RS_NORMAL, 2),
1117
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1118
                       ],
1119
                      [(constants.RS_NORMAL, 3),
1120
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1121
                       ],
1122
                      ])
1123

    
1124
  def testDiskparams(self):
1125
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1126
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1127

    
1128
    self.assertEqual(q.RequestedData(),
1129
                     set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1130

    
1131
    self.assertEqual(q.Query(gqd),
1132
      [[(constants.RS_NORMAL, "default"),
1133
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1134
        (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1135
        (constants.RS_NORMAL, {}),
1136
        ],
1137
       [(constants.RS_NORMAL, "restricted"),
1138
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1139
        (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1140
                                                     self.custom_diskparams)),
1141
        (constants.RS_NORMAL, self.custom_diskparams),
1142
        ],
1143
       ])
1144

    
1145

    
1146
class TestOsQuery(unittest.TestCase):
1147
  def _Create(self, selected):
1148
    return query.Query(query.OS_FIELDS, selected)
1149

    
1150
  def test(self):
1151
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1152
    api_versions = [10, 0, 15, 5]
1153
    parameters = ["zpar3", "apar9"]
1154

    
1155
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1156
    assert (api_versions != sorted(api_versions) and
1157
            api_versions != utils.NiceSort(variants))
1158
    assert (parameters != sorted(parameters) and
1159
            parameters != utils.NiceSort(parameters))
1160

    
1161
    data = [
1162
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1163
                   variants=set(), api_versions=set(), parameters=set(),
1164
                   node_status={ "some": "status", }),
1165
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1166
                   variants=set(variants),
1167
                   api_versions=set(api_versions),
1168
                   parameters=set(parameters),
1169
                   node_status={ "some": "other", "status": None, }),
1170
      ]
1171

    
1172

    
1173
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1174
                      "api_versions", "parameters", "node_status"])
1175
    self.assertEqual(q.RequestedData(), set([]))
1176
    self.assertEqual(q.Query(data),
1177
                     [[(constants.RS_NORMAL, "debian"),
1178
                       (constants.RS_NORMAL, False),
1179
                       (constants.RS_NORMAL, False),
1180
                       (constants.RS_NORMAL, False),
1181
                       (constants.RS_NORMAL, []),
1182
                       (constants.RS_NORMAL, []),
1183
                       (constants.RS_NORMAL, []),
1184
                       (constants.RS_NORMAL, {"some": "status"})],
1185
                      [(constants.RS_NORMAL, "dos"),
1186
                       (constants.RS_NORMAL, True),
1187
                       (constants.RS_NORMAL, False),
1188
                       (constants.RS_NORMAL, True),
1189
                       (constants.RS_NORMAL,
1190
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1191
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1192
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1193
                       (constants.RS_NORMAL,
1194
                        { "some": "other", "status": None, })
1195
                       ]])
1196

    
1197

    
1198
class TestQueryFields(unittest.TestCase):
1199
  def testAllFields(self):
1200
    for fielddefs in query.ALL_FIELD_LISTS:
1201
      result = query.QueryFields(fielddefs, None)
1202
      self.assert_(isinstance(result, dict))
1203
      response = objects.QueryFieldsResponse.FromDict(result)
1204
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1205
        [(fdef2.name, fdef2.title)
1206
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1207
                                                key=lambda x: x[0].name)])
1208

    
1209
  def testSomeFields(self):
1210
    rnd = random.Random(5357)
1211

    
1212
    for _ in range(10):
1213
      for fielddefs in query.ALL_FIELD_LISTS:
1214
        if len(fielddefs) > 20:
1215
          sample_size = rnd.randint(5, 20)
1216
        else:
1217
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1218
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1219
                                                         sample_size)]
1220
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1221
        self.assert_(isinstance(result, dict))
1222
        response = objects.QueryFieldsResponse.FromDict(result)
1223
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1224
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1225

    
1226

    
1227
class TestQueryFilter(unittest.TestCase):
1228
  def testRequestedNames(self):
1229
    for (what, fielddefs) in query.ALL_FIELDS.items():
1230
      if what == constants.QR_JOB:
1231
        namefield = "id"
1232
        nameval = 123
1233
        namevalempty = 0
1234
        genval = lambda i: i * 10
1235
        randvals = [17361, 22015, 13193, 15215]
1236
      else:
1237
        nameval = "abc"
1238
        namevalempty = ""
1239
        genval = lambda i: "x%s" % i
1240
        randvals = ["x17361", "x22015", "x13193", "x15215"]
1241
        if what == constants.QR_EXPORT:
1242
          namefield = "export"
1243
        else:
1244
          namefield = "name"
1245

    
1246
      assert namefield in fielddefs
1247

    
1248
      reqnames = [genval(i) for i in range(4)]
1249
      innerfilter = [["=", namefield, v] for v in reqnames]
1250

    
1251
      # No name field
1252
      q = query.Query(fielddefs, [namefield],
1253
                      qfilter=["=", namefield, nameval], namefield=None)
1254
      self.assertEqual(q.RequestedNames(), None)
1255

    
1256
      # No filter
1257
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1258
      self.assertEqual(q.RequestedNames(), None)
1259

    
1260
      # Check empty query
1261
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1262
                      namefield=namefield)
1263
      self.assertEqual(q.RequestedNames(), None)
1264

    
1265
      # Check order
1266
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1267
                      namefield=namefield)
1268
      self.assertEqual(q.RequestedNames(), reqnames)
1269

    
1270
      # Check reverse order
1271
      q = query.Query(fielddefs, [namefield],
1272
                      qfilter=["|"] + list(reversed(innerfilter)),
1273
                      namefield=namefield)
1274
      self.assertEqual(q.RequestedNames(), list(reversed(reqnames)))
1275

    
1276
      # Duplicates
1277
      q = query.Query(fielddefs, [namefield],
1278
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1279
                      namefield=namefield)
1280
      self.assertEqual(q.RequestedNames(), reqnames)
1281

    
1282
      # Unknown name field
1283
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1284
                        namefield="_unknown_field_")
1285

    
1286
      # Filter with AND
1287
      q = query.Query(fielddefs, [namefield],
1288
                      qfilter=["|", ["=", namefield, nameval],
1289
                                    ["&", ["=", namefield, namevalempty]]],
1290
                      namefield=namefield)
1291
      self.assertTrue(q.RequestedNames() is None)
1292

    
1293
      # Filter with NOT
1294
      q = query.Query(fielddefs, [namefield],
1295
                      qfilter=["|", ["=", namefield, nameval],
1296
                                    ["!", ["=", namefield, namevalempty]]],
1297
                      namefield=namefield)
1298
      self.assertTrue(q.RequestedNames() is None)
1299

    
1300
      # Filter with only OR (names must be in correct order)
1301
      q = query.Query(fielddefs, [namefield],
1302
                      qfilter=["|", ["=", namefield, randvals[0]],
1303
                                    ["|", ["=", namefield, randvals[1]]],
1304
                                    ["|", ["|", ["=", namefield, randvals[2]]]],
1305
                                    ["=", namefield, randvals[3]]],
1306
                      namefield=namefield)
1307
      self.assertEqual(q.RequestedNames(), randvals)
1308

    
1309
  @staticmethod
1310
  def _GenNestedFilter(namefield, op, depth, nameval):
1311
    nested = ["=", namefield, nameval]
1312
    for i in range(depth):
1313
      nested = [op, nested]
1314
    return nested
1315

    
1316
  def testCompileFilter(self):
1317
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1318

    
1319
    for (what, fielddefs) in query.ALL_FIELDS.items():
1320
      if what == constants.QR_JOB:
1321
        namefield = "id"
1322
        nameval = 123
1323
      elif what == constants.QR_EXPORT:
1324
        namefield = "export"
1325
        nameval = "value"
1326
      else:
1327
        namefield = "name"
1328
        nameval = "value"
1329

    
1330
      checks = [
1331
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1332
        ["=", "_unknown_field", "value"],
1333
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1334
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1335
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1336
        ]
1337

    
1338
      for qfilter in checks:
1339
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1340
                          fielddefs, None, qfilter)
1341

    
1342
      for op in ["|", "!"]:
1343
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1344
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1345
                                                      qfilter)))
1346

    
1347
  def testQueryInputOrder(self):
1348
    fielddefs = query._PrepareFieldList([
1349
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1350
       None, 0, lambda ctx, item: item["pnode"]),
1351
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1352
       None, 0, lambda ctx, item: item["snode"]),
1353
      ], [])
1354

    
1355
    data = [
1356
      { "pnode": "node1", "snode": "node44", },
1357
      { "pnode": "node30", "snode": "node90", },
1358
      { "pnode": "node25", "snode": "node1", },
1359
      { "pnode": "node20", "snode": "node1", },
1360
      ]
1361

    
1362
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1363

    
1364
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1365
                    qfilter=qfilter)
1366
    self.assertTrue(q.RequestedNames() is None)
1367
    self.assertFalse(q.RequestedData())
1368
    self.assertEqual(q.Query(data),
1369
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1370
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1371
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1372

    
1373
    # Try again with reversed input data
1374
    self.assertEqual(q.Query(reversed(data)),
1375
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1376
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1377
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1378

    
1379
    # No name field, result must be in incoming order
1380
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1381
                    qfilter=qfilter)
1382
    self.assertFalse(q.RequestedData())
1383
    self.assertEqual(q.Query(data),
1384
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1385
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1386
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1387
    self.assertEqual(q.OldStyleQuery(data), [
1388
      ["node1", "node44"],
1389
      ["node25", "node1"],
1390
      ["node20", "node1"],
1391
      ])
1392
    self.assertEqual(q.Query(reversed(data)),
1393
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1394
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1395
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1396
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1397
      ["node20", "node1"],
1398
      ["node25", "node1"],
1399
      ["node1", "node44"],
1400
      ])
1401

    
1402
    # Name field, but no sorting, result must be in incoming order
1403
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1404
    self.assertFalse(q.RequestedData())
1405
    self.assertEqual(q.Query(data, sort_by_name=False),
1406
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1407
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1408
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1409
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1410
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1411
      ["node1", "node44"],
1412
      ["node30", "node90"],
1413
      ["node25", "node1"],
1414
      ["node20", "node1"],
1415
      ])
1416
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1417
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1418
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1419
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1420
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1421
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1422
      ["node20", "node1"],
1423
      ["node25", "node1"],
1424
      ["node30", "node90"],
1425
      ["node1", "node44"],
1426
      ])
1427

    
1428
  def testEqualNamesOrder(self):
1429
    fielddefs = query._PrepareFieldList([
1430
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1431
       None, 0, lambda ctx, item: item["pnode"]),
1432
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1433
       None, 0, lambda ctx, item: item["num"]),
1434
      ], [])
1435

    
1436
    data = [
1437
      { "pnode": "node1", "num": 100, },
1438
      { "pnode": "node1", "num": 25, },
1439
      { "pnode": "node2", "num": 90, },
1440
      { "pnode": "node2", "num": 30, },
1441
      ]
1442

    
1443
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1444
                    qfilter=["|", ["=", "pnode", "node1"],
1445
                                  ["=", "pnode", "node2"],
1446
                                  ["=", "pnode", "node1"]])
1447
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1448
                     msg="Did not return unique names")
1449
    self.assertFalse(q.RequestedData())
1450
    self.assertEqual(q.Query(data),
1451
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1452
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1453
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1454
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1455
    self.assertEqual(q.Query(data, sort_by_name=False),
1456
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1457
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1458
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1459
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1460

    
1461
    data = [
1462
      { "pnode": "nodeX", "num": 50, },
1463
      { "pnode": "nodeY", "num": 40, },
1464
      { "pnode": "nodeX", "num": 30, },
1465
      { "pnode": "nodeX", "num": 20, },
1466
      { "pnode": "nodeM", "num": 10, },
1467
      ]
1468

    
1469
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1470
                    qfilter=["|", ["=", "pnode", "nodeX"],
1471
                                  ["=", "pnode", "nodeY"],
1472
                                  ["=", "pnode", "nodeY"],
1473
                                  ["=", "pnode", "nodeY"],
1474
                                  ["=", "pnode", "nodeM"]])
1475
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1476
                     msg="Did not return unique names")
1477
    self.assertFalse(q.RequestedData())
1478

    
1479
    # First sorted by name, then input order
1480
    self.assertEqual(q.Query(data, sort_by_name=True),
1481
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1482
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1483
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1484
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1485
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1486

    
1487
    # Input order
1488
    self.assertEqual(q.Query(data, sort_by_name=False),
1489
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1490
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1491
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1492
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1493
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1494

    
1495
  def testFilter(self):
1496
    (DK_A, DK_B) = range(1000, 1002)
1497

    
1498
    fielddefs = query._PrepareFieldList([
1499
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1500
       DK_A, 0, lambda ctx, item: item["name"]),
1501
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1502
       DK_B, 0, lambda ctx, item: item["other"]),
1503
      ], [])
1504

    
1505
    data = [
1506
      { "name": "node1", "other": "foo", },
1507
      { "name": "node2", "other": "bar", },
1508
      { "name": "node3", "other": "Hello", },
1509
      ]
1510

    
1511
    # Empty filter
1512
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1513
                    qfilter=["|"])
1514
    self.assertTrue(q.RequestedNames() is None)
1515
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1516
    self.assertEqual(q.Query(data), [])
1517

    
1518
    # Normal filter
1519
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1520
                    qfilter=["=", "name", "node1"])
1521
    self.assertEqual(q.RequestedNames(), ["node1"])
1522
    self.assertEqual(q.Query(data),
1523
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1524

    
1525
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1526
                    qfilter=(["|", ["=", "name", "node1"],
1527
                                   ["=", "name", "node3"]]))
1528
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1529
    self.assertEqual(q.Query(data),
1530
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1531
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1532

    
1533
    # Complex filter
1534
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1535
                    qfilter=(["|", ["=", "name", "node1"],
1536
                                   ["|", ["=", "name", "node3"],
1537
                                         ["=", "name", "node2"]],
1538
                                   ["=", "name", "node3"]]))
1539
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1540
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1541
    self.assertEqual(q.Query(data),
1542
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1543
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1544
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1545

    
1546
    # Filter data type mismatch
1547
    for i in [-1, 0, 1, 123, [], None, True, False]:
1548
      self.assertRaises(errors.ParameterError, query.Query,
1549
                        fielddefs, ["name", "other"], namefield="name",
1550
                        qfilter=["=", "name", i])
1551

    
1552
    # Negative filter
1553
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1554
                    qfilter=["!", ["|", ["=", "name", "node1"],
1555
                                        ["=", "name", "node3"]]])
1556
    self.assertTrue(q.RequestedNames() is None)
1557
    self.assertEqual(q.Query(data),
1558
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1559

    
1560
    # Not equal
1561
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1562
                    qfilter=["!=", "name", "node3"])
1563
    self.assertTrue(q.RequestedNames() is None)
1564
    self.assertEqual(q.Query(data),
1565
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1566
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1567

    
1568
    # Data type
1569
    q = query.Query(fielddefs, [], namefield="name",
1570
                    qfilter=["|", ["=", "other", "bar"],
1571
                                  ["=", "name", "foo"]])
1572
    self.assertTrue(q.RequestedNames() is None)
1573
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1574
    self.assertEqual(q.Query(data), [[]])
1575

    
1576
    # Only one data type
1577
    q = query.Query(fielddefs, ["other"], namefield="name",
1578
                    qfilter=["=", "other", "bar"])
1579
    self.assertTrue(q.RequestedNames() is None)
1580
    self.assertEqual(q.RequestedData(), set([DK_B]))
1581
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1582

    
1583
    q = query.Query(fielddefs, [], namefield="name",
1584
                    qfilter=["=", "other", "bar"])
1585
    self.assertTrue(q.RequestedNames() is None)
1586
    self.assertEqual(q.RequestedData(), set([DK_B]))
1587
    self.assertEqual(q.Query(data), [[]])
1588

    
1589
    # Data type in boolean operator
1590
    q = query.Query(fielddefs, [], namefield="name",
1591
                    qfilter=["?", "name"])
1592
    self.assertTrue(q.RequestedNames() is None)
1593
    self.assertEqual(q.RequestedData(), set([DK_A]))
1594
    self.assertEqual(q.Query(data), [[], [], []])
1595

    
1596
    q = query.Query(fielddefs, [], namefield="name",
1597
                    qfilter=["!", ["?", "name"]])
1598
    self.assertTrue(q.RequestedNames() is None)
1599
    self.assertEqual(q.RequestedData(), set([DK_A]))
1600
    self.assertEqual(q.Query(data), [])
1601

    
1602
  def testFilterContains(self):
1603
    fielddefs = query._PrepareFieldList([
1604
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1605
       None, 0, lambda ctx, item: item["name"]),
1606
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1607
       None, 0, lambda ctx, item: item["other"]),
1608
      ], [])
1609

    
1610
    data = [
1611
      { "name": "node2", "other": ["x", "y", "bar"], },
1612
      { "name": "node3", "other": "Hello", },
1613
      { "name": "node1", "other": ["a", "b", "foo"], },
1614
      { "name": "empty", "other": []},
1615
      ]
1616

    
1617
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1618
                    qfilter=["=[]", "other", "bar"])
1619
    self.assertTrue(q.RequestedNames() is None)
1620
    self.assertEqual(q.Query(data), [
1621
      [(constants.RS_NORMAL, "node2"),
1622
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1623
      ])
1624

    
1625
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1626
                    qfilter=["|", ["=[]", "other", "bar"],
1627
                                  ["=[]", "other", "a"],
1628
                                  ["=[]", "other", "b"]])
1629
    self.assertTrue(q.RequestedNames() is None)
1630
    self.assertEqual(q.Query(data), [
1631
      [(constants.RS_NORMAL, "node1"),
1632
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1633
      [(constants.RS_NORMAL, "node2"),
1634
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1635
      ])
1636
    self.assertEqual(q.OldStyleQuery(data), [
1637
      ["node1", ["a", "b", "foo"]],
1638
      ["node2", ["x", "y", "bar"]],
1639
      ])
1640

    
1641
    # Boolean test
1642
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1643
                    qfilter=["?", "other"])
1644
    self.assertEqual(q.OldStyleQuery(data), [
1645
      ["node1", ["a", "b", "foo"]],
1646
      ["node2", ["x", "y", "bar"]],
1647
      ["node3", "Hello"],
1648
      ])
1649

    
1650
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1651
                    qfilter=["!", ["?", "other"]])
1652
    self.assertEqual(q.OldStyleQuery(data), [
1653
      ["empty", []],
1654
      ])
1655

    
1656
  def testFilterHostname(self):
1657
    fielddefs = query._PrepareFieldList([
1658
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1659
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1660
      ], [])
1661

    
1662
    data = [
1663
      { "name": "node1.example.com", },
1664
      { "name": "node2.example.com", },
1665
      { "name": "node2.example.net", },
1666
      ]
1667

    
1668
    q = query.Query(fielddefs, ["name"], namefield="name",
1669
                    qfilter=["=", "name", "node2"])
1670
    self.assertEqual(q.RequestedNames(), ["node2"])
1671
    self.assertEqual(q.Query(data), [
1672
      [(constants.RS_NORMAL, "node2.example.com")],
1673
      [(constants.RS_NORMAL, "node2.example.net")],
1674
      ])
1675

    
1676
    q = query.Query(fielddefs, ["name"], namefield="name",
1677
                    qfilter=["=", "name", "node1"])
1678
    self.assertEqual(q.RequestedNames(), ["node1"])
1679
    self.assertEqual(q.Query(data), [
1680
      [(constants.RS_NORMAL, "node1.example.com")],
1681
      ])
1682

    
1683
    q = query.Query(fielddefs, ["name"], namefield="name",
1684
                    qfilter=["=", "name", "othername"])
1685
    self.assertEqual(q.RequestedNames(), ["othername"])
1686
    self.assertEqual(q.Query(data), [])
1687

    
1688
    q = query.Query(fielddefs, ["name"], namefield="name",
1689
                    qfilter=["|", ["=", "name", "node1.example.com"],
1690
                                  ["=", "name", "node2"]])
1691
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1692
    self.assertEqual(q.Query(data), [
1693
      [(constants.RS_NORMAL, "node1.example.com")],
1694
      [(constants.RS_NORMAL, "node2.example.com")],
1695
      [(constants.RS_NORMAL, "node2.example.net")],
1696
      ])
1697
    self.assertEqual(q.OldStyleQuery(data), [
1698
      ["node1.example.com"],
1699
      ["node2.example.com"],
1700
      ["node2.example.net"],
1701
      ])
1702

    
1703
    q = query.Query(fielddefs, ["name"], namefield="name",
1704
                    qfilter=["!=", "name", "node1"])
1705
    self.assertTrue(q.RequestedNames() is None)
1706
    self.assertEqual(q.Query(data), [
1707
      [(constants.RS_NORMAL, "node2.example.com")],
1708
      [(constants.RS_NORMAL, "node2.example.net")],
1709
      ])
1710
    self.assertEqual(q.OldStyleQuery(data), [
1711
      ["node2.example.com"],
1712
      ["node2.example.net"],
1713
      ])
1714

    
1715
  def testFilterBoolean(self):
1716
    fielddefs = query._PrepareFieldList([
1717
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1718
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1719
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1720
       None, 0, lambda ctx, item: item["value"]),
1721
      ], [])
1722

    
1723
    data = [
1724
      { "name": "node1", "value": False, },
1725
      { "name": "node2", "value": True, },
1726
      { "name": "node3", "value": True, },
1727
      ]
1728

    
1729
    q = query.Query(fielddefs, ["name", "value"],
1730
                    qfilter=["|", ["=", "value", False],
1731
                                  ["=", "value", True]])
1732
    self.assertTrue(q.RequestedNames() is None)
1733
    self.assertEqual(q.Query(data), [
1734
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1735
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1736
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1737
      ])
1738

    
1739
    q = query.Query(fielddefs, ["name", "value"],
1740
                    qfilter=["|", ["=", "value", False],
1741
                                  ["!", ["=", "value", False]]])
1742
    self.assertTrue(q.RequestedNames() is None)
1743
    self.assertEqual(q.Query(data), [
1744
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1745
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1746
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1747
      ])
1748

    
1749
    # Comparing bool with string
1750
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1751
      self.assertRaises(errors.ParameterError, query.Query,
1752
                        fielddefs, ["name", "value"],
1753
                        qfilter=["=", "value", i])
1754

    
1755
    # Truth filter
1756
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1757
    self.assertTrue(q.RequestedNames() is None)
1758
    self.assertEqual(q.Query(data), [
1759
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1760
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1761
      ])
1762

    
1763
    # Negative bool filter
1764
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1765
    self.assertTrue(q.RequestedNames() is None)
1766
    self.assertEqual(q.Query(data), [
1767
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1768
      ])
1769

    
1770
    # Complex truth filter
1771
    q = query.Query(fielddefs, ["name", "value"],
1772
                    qfilter=["|", ["&", ["=", "name", "node1"],
1773
                                        ["!", ["?", "value"]]],
1774
                                  ["?", "value"]])
1775
    self.assertTrue(q.RequestedNames() is None)
1776
    self.assertEqual(q.Query(data), [
1777
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1778
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1779
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1780
      ])
1781

    
1782
  def testFilterRegex(self):
1783
    fielddefs = query._PrepareFieldList([
1784
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1785
       None, 0, lambda ctx, item: item["name"]),
1786
      ], [])
1787

    
1788
    data = [
1789
      { "name": "node1.example.com", },
1790
      { "name": "node2.site.example.com", },
1791
      { "name": "node2.example.net", },
1792

    
1793
      # Empty name
1794
      { "name": "", },
1795
      ]
1796

    
1797
    q = query.Query(fielddefs, ["name"], namefield="name",
1798
                    qfilter=["=~", "name", "site"])
1799
    self.assertTrue(q.RequestedNames() is None)
1800
    self.assertEqual(q.Query(data), [
1801
      [(constants.RS_NORMAL, "node2.site.example.com")],
1802
      ])
1803

    
1804
    q = query.Query(fielddefs, ["name"], namefield="name",
1805
                    qfilter=["=~", "name", "^node2"])
1806
    self.assertTrue(q.RequestedNames() is None)
1807
    self.assertEqual(q.Query(data), [
1808
      [(constants.RS_NORMAL, "node2.example.net")],
1809
      [(constants.RS_NORMAL, "node2.site.example.com")],
1810
      ])
1811

    
1812
    q = query.Query(fielddefs, ["name"], namefield="name",
1813
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1814
    self.assertTrue(q.RequestedNames() is None)
1815
    self.assertEqual(q.Query(data), [
1816
      [(constants.RS_NORMAL, "node1.example.com")],
1817
      [(constants.RS_NORMAL, "node2.site.example.com")],
1818
      ])
1819

    
1820
    q = query.Query(fielddefs, ["name"], namefield="name",
1821
                    qfilter=["=~", "name", r"."])
1822
    self.assertTrue(q.RequestedNames() is None)
1823
    self.assertEqual(q.Query(data), [
1824
      [(constants.RS_NORMAL, "node1.example.com")],
1825
      [(constants.RS_NORMAL, "node2.example.net")],
1826
      [(constants.RS_NORMAL, "node2.site.example.com")],
1827
      ])
1828

    
1829
    q = query.Query(fielddefs, ["name"], namefield="name",
1830
                    qfilter=["=~", "name", r"^$"])
1831
    self.assertTrue(q.RequestedNames() is None)
1832
    self.assertEqual(q.Query(data), [
1833
      [(constants.RS_NORMAL, "")],
1834
      ])
1835

    
1836
    # Invalid regular expression
1837
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1838
                      qfilter=["=~", "name", r"["])
1839

    
1840
  def testFilterLessGreater(self):
1841
    fielddefs = query._PrepareFieldList([
1842
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1843
       None, 0, lambda ctx, item: item),
1844
      ], [])
1845

    
1846
    data = range(100)
1847

    
1848
    q = query.Query(fielddefs, ["value"],
1849
                    qfilter=["<", "value", 20])
1850
    self.assertTrue(q.RequestedNames() is None)
1851
    self.assertEqual(q.Query(data),
1852
                     [[(constants.RS_NORMAL, i)] for i in range(20)])
1853

    
1854
    q = query.Query(fielddefs, ["value"],
1855
                    qfilter=["<=", "value", 30])
1856
    self.assertTrue(q.RequestedNames() is None)
1857
    self.assertEqual(q.Query(data),
1858
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1859

    
1860
    q = query.Query(fielddefs, ["value"],
1861
                    qfilter=[">", "value", 40])
1862
    self.assertTrue(q.RequestedNames() is None)
1863
    self.assertEqual(q.Query(data),
1864
                     [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
1865

    
1866
    q = query.Query(fielddefs, ["value"],
1867
                    qfilter=[">=", "value", 50])
1868
    self.assertTrue(q.RequestedNames() is None)
1869
    self.assertEqual(q.Query(data),
1870
                     [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
1871

    
1872
  def testFilterLessGreaterJobId(self):
1873
    fielddefs = query._PrepareFieldList([
1874
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1875
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1876
      ], [])
1877

    
1878
    data = ["1", "2", "3", "10", "102", "120", "125", "15", "100", "7"]
1879

    
1880
    assert data != utils.NiceSort(data), "Test data should not be sorted"
1881

    
1882
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1883
    self.assertTrue(q.RequestedNames() is None)
1884
    self.assertEqual(q.Query(data), [
1885
      [(constants.RS_NORMAL, "1")],
1886
      [(constants.RS_NORMAL, "2")],
1887
      [(constants.RS_NORMAL, "3")],
1888
      [(constants.RS_NORMAL, "10")],
1889
      [(constants.RS_NORMAL, "15")],
1890
      [(constants.RS_NORMAL, "7")],
1891
      ])
1892

    
1893
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1894
    self.assertTrue(q.RequestedNames() is None)
1895
    self.assertEqual(q.Query(data), [
1896
      [(constants.RS_NORMAL, "102")],
1897
      [(constants.RS_NORMAL, "120")],
1898
      [(constants.RS_NORMAL, "125")],
1899
      [(constants.RS_NORMAL, "100")],
1900
      ])
1901

    
1902
    # Integers are no valid job IDs
1903
    self.assertRaises(errors.ParameterError, query.Query,
1904
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1905

    
1906
  def testFilterLessGreaterSplitTimestamp(self):
1907
    fielddefs = query._PrepareFieldList([
1908
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1909
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1910
      ], [])
1911

    
1912
    data = [
1913
      utils.SplitTime(0),
1914
      utils.SplitTime(0.1),
1915
      utils.SplitTime(18224.7872),
1916
      utils.SplitTime(919896.12623),
1917
      utils.SplitTime(999),
1918
      utils.SplitTime(989.9999),
1919
      ]
1920

    
1921
    for i in [0, [0, 0]]:
1922
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1923
      self.assertTrue(q.RequestedNames() is None)
1924
      self.assertEqual(q.Query(data), [])
1925

    
1926
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1927
    self.assertTrue(q.RequestedNames() is None)
1928
    self.assertEqual(q.Query(data), [
1929
      [(constants.RS_NORMAL, (0, 0))],
1930
      [(constants.RS_NORMAL, (0, 100000))],
1931
      [(constants.RS_NORMAL, (999, 0))],
1932
      [(constants.RS_NORMAL, (989, 999900))],
1933
      ])
1934

    
1935
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
1936
    self.assertTrue(q.RequestedNames() is None)
1937
    self.assertEqual(q.Query(data), [
1938
      [(constants.RS_NORMAL, (18224, 787200))],
1939
      [(constants.RS_NORMAL, (919896, 126230))],
1940
      ])
1941

    
1942
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1943
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1944
      self.assertTrue(q.RequestedNames() is None)
1945
      self.assertEqual(q.Query(data), [
1946
        [(constants.RS_NORMAL, (18224, 787200))],
1947
        [(constants.RS_NORMAL, (919896, 126230))],
1948
        ])
1949

    
1950
    q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880])
1951
    self.assertTrue(q.RequestedNames() is None)
1952
    self.assertEqual(q.Query(data), [
1953
      [(constants.RS_NORMAL, (919896, 126230))],
1954
      ])
1955

    
1956

    
1957
if __name__ == "__main__":
1958
  testutils.GanetiTestProgram()