Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.query_unittest.py @ 6ccce5d4

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

    
835
    assert not utils.FindDuplicates(inst.uuid for inst in instances)
836
    assert not utils.FindDuplicates(inst.name for inst in instances)
837

    
838
    instbyname = dict((inst.name, inst) for inst in instances)
839

    
840
    disk_usage = dict((inst.uuid,
841
                       gmi.ComputeDiskSize(inst.disk_template,
842
                                           [{"size": disk.size}
843
                                           for disk in inst.disks]))
844
                      for inst in instances)
845

    
846
    inst_bridges = {
847
      "inst3-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
848
      "inst4-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
849
                     None, "eth123"],
850
      }
851

    
852
    live_data = {
853
      "inst2-uuid": {
854
        "vcpus": 3,
855
        "state": hv_base.HvInstanceState.RUNNING,
856
        },
857
      "inst4-uuid": {
858
        "memory": 123,
859
        "state": hv_base.HvInstanceState.RUNNING,
860
        },
861
      "inst6-uuid": {
862
        "memory": 768,
863
        "state": hv_base.HvInstanceState.RUNNING,
864
        },
865
      "inst7-uuid": {
866
        "vcpus": 3,
867
        "state": hv_base.HvInstanceState.RUNNING,
868
        },
869
      "inst9-uuid": {
870
        "vcpus": 3,
871
        "state": hv_base.HvInstanceState.SHUTDOWN,
872
        },
873
      }
874
    wrongnode_inst = set(["inst7-uuid"])
875

    
876
    consinfo = dict((inst.uuid, None) for inst in instances)
877
    consinfo["inst7-uuid"] = \
878
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
879
                              host=instbyname["inst7"].primary_node,
880
                              user="root",
881
                              command=["hostname"]).ToDict()
882

    
883
    nodes = dict([(uuid, objects.Node(
884
                           name="%s.example.com" % uuid,
885
                           uuid=uuid,
886
                           group="default-uuid"))
887
                  for uuid in node_uuids])
888

    
889
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
890
                                  offline_nodes, bad_nodes, live_data,
891
                                  wrongnode_inst, consinfo, nodes, {}, {})
892
    result = q.Query(iqd)
893
    self.assertEqual(len(result), len(instances))
894
    self.assert_(compat.all(len(row) == len(selected)
895
                            for row in result))
896

    
897
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
898
           "Offline nodes not included in bad nodes"
899

    
900
    tested_status = set()
901

    
902
    for (inst, row) in zip(instances, result):
903
      assert inst.primary_node in node_uuids
904

    
905
      self.assertEqual(row[fieldidx["name"]],
906
                       (constants.RS_NORMAL, inst.name))
907

    
908
      if inst.primary_node in offline_nodes:
909
        exp_status = constants.INSTST_NODEOFFLINE
910
      elif inst.primary_node in bad_nodes:
911
        exp_status = constants.INSTST_NODEDOWN
912
      elif inst.uuid in live_data:
913
        if inst.uuid in wrongnode_inst:
914
          exp_status = constants.INSTST_WRONGNODE
915
        else:
916
          instance_state = live_data[inst.uuid]["state"]
917
          if hv_base.HvInstanceState.IsShutdown(instance_state):
918
            if inst.admin_state == constants.ADMINST_UP:
919
              exp_status = constants.INSTST_USERDOWN
920
            else:
921
              exp_status = constants.INSTST_ADMINDOWN
922
          else:
923
            if inst.admin_state == constants.ADMINST_UP:
924
              exp_status = constants.INSTST_RUNNING
925
            else:
926
              exp_status = constants.INSTST_ERRORUP
927
      else:
928
        if inst.admin_state == constants.ADMINST_UP:
929
          exp_status = constants.INSTST_ERRORDOWN
930
        elif inst.admin_state == constants.ADMINST_DOWN:
931
          exp_status = constants.INSTST_ADMINDOWN
932
        else:
933
          exp_status = constants.INSTST_ADMINOFFLINE
934

    
935
      self.assertEqual(row[fieldidx["status"]],
936
                       (constants.RS_NORMAL, exp_status))
937

    
938
      (_, status) = row[fieldidx["status"]]
939
      tested_status.add(status)
940

    
941
      #FIXME(dynmem): check oper_ram vs min/max mem
942
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
943
        if inst.primary_node in bad_nodes:
944
          exp = (constants.RS_NODATA, None)
945
        elif inst.uuid in live_data:
946
          value = live_data[inst.uuid].get(livefield, None)
947
          if value is None:
948
            exp = (constants.RS_UNAVAIL, None)
949
          else:
950
            exp = (constants.RS_NORMAL, value)
951
        else:
952
          exp = (constants.RS_UNAVAIL, None)
953

    
954
        self.assertEqual(row[fieldidx[field]], exp)
955

    
956
      bridges = inst_bridges.get(inst.uuid, [])
957
      self.assertEqual(row[fieldidx["nic.bridges"]],
958
                       (constants.RS_NORMAL, bridges))
959
      if bridges:
960
        self.assertEqual(row[fieldidx["bridge"]],
961
                         (constants.RS_NORMAL, bridges[0]))
962
      else:
963
        self.assertEqual(row[fieldidx["bridge"]],
964
                         (constants.RS_UNAVAIL, None))
965

    
966
      for i in range(constants.MAX_NICS):
967
        if i < len(bridges) and bridges[i] is not None:
968
          exp = (constants.RS_NORMAL, bridges[i])
969
        else:
970
          exp = (constants.RS_UNAVAIL, None)
971
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
972

    
973
      if inst.primary_node in bad_nodes:
974
        exp = (constants.RS_NODATA, None)
975
      else:
976
        exp = (constants.RS_NORMAL, inst.uuid in live_data)
977
      self.assertEqual(row[fieldidx["oper_state"]], exp)
978

    
979
      cust_exp = (constants.RS_NORMAL, {})
980
      if inst.os == "deb99":
981
        if inst.uuid == "inst6-uuid":
982
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
983
          cust_exp = exp
984
        else:
985
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
986
      else:
987
        exp = (constants.RS_NORMAL, {})
988
      self.assertEqual(row[fieldidx["osparams"]], exp)
989
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
990

    
991
      usage = disk_usage[inst.uuid]
992
      if usage is None:
993
        usage = 0
994
      self.assertEqual(row[fieldidx["disk_usage"]],
995
                       (constants.RS_NORMAL, usage))
996

    
997
      for alias, target in [("sda_size", "disk.size/0"),
998
                            ("sdb_size", "disk.size/1"),
999
                            ("vcpus", "be/vcpus"),
1000
                            ("ip", "nic.ip/0"),
1001
                            ("mac", "nic.mac/0"),
1002
                            ("bridge", "nic.bridge/0"),
1003
                            ("nic_mode", "nic.mode/0"),
1004
                            ("nic_link", "nic.link/0"),
1005
                            ]:
1006
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
1007

    
1008
      for field in ["ctime", "mtime"]:
1009
        if getattr(inst, field) is None:
1010
          # No ctime/mtime
1011
          exp = (constants.RS_UNAVAIL, None)
1012
        else:
1013
          exp = (constants.RS_NORMAL, getattr(inst, field))
1014
        self.assertEqual(row[fieldidx[field]], exp)
1015

    
1016
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
1017

    
1018
    # Ensure all possible status' have been tested
1019
    self.assertEqual(tested_status, set(constants.INSTST_ALL))
1020

    
1021
  def _CheckInstanceConsole(self, instance, (status, consdata)):
1022
    if instance.name == "inst7":
1023
      self.assertEqual(status, constants.RS_NORMAL)
1024
      console = objects.InstanceConsole.FromDict(consdata)
1025
      self.assertTrue(console.Validate())
1026
      self.assertEqual(console.host, instance.primary_node)
1027
    else:
1028
      self.assertEqual(status, constants.RS_UNAVAIL)
1029

    
1030

    
1031
class TestGroupQuery(unittest.TestCase):
1032

    
1033
  def setUp(self):
1034
    self.custom_diskparams = {
1035
      constants.DT_DRBD8: {
1036
        constants.DRBD_DEFAULT_METAVG: "foobar",
1037
      },
1038
    }
1039

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

    
1069
  def _Create(self, selected):
1070
    return query.Query(query.GROUP_FIELDS, selected)
1071

    
1072
  def testSimple(self):
1073
    q = self._Create(["name", "uuid", "alloc_policy"])
1074
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1075

    
1076
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1077

    
1078
    self.assertEqual(q.Query(gqd),
1079
      [[(constants.RS_NORMAL, "default"),
1080
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1081
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1082
        ],
1083
       [(constants.RS_NORMAL, "restricted"),
1084
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1085
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1086
        ],
1087
       ])
1088

    
1089
  def testNodes(self):
1090
    groups_to_nodes = {
1091
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1092
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1093
      }
1094

    
1095
    q = self._Create(["name", "node_cnt", "node_list"])
1096
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1097
                               False)
1098

    
1099
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1100

    
1101
    self.assertEqual(q.Query(gqd),
1102
                     [[(constants.RS_NORMAL, "default"),
1103
                       (constants.RS_NORMAL, 2),
1104
                       (constants.RS_NORMAL, ["node1", "node2"]),
1105
                       ],
1106
                      [(constants.RS_NORMAL, "restricted"),
1107
                       (constants.RS_NORMAL, 3),
1108
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1109
                       ],
1110
                      ])
1111

    
1112
  def testInstances(self):
1113
    groups_to_instances = {
1114
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1115
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1116
      }
1117

    
1118
    q = self._Create(["pinst_cnt", "pinst_list"])
1119
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1120
      groups_to_instances, False)
1121

    
1122
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1123

    
1124
    self.assertEqual(q.Query(gqd),
1125
                     [[(constants.RS_NORMAL, 2),
1126
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1127
                       ],
1128
                      [(constants.RS_NORMAL, 3),
1129
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1130
                       ],
1131
                      ])
1132

    
1133
  def testDiskparams(self):
1134
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1135
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1136

    
1137
    self.assertEqual(q.RequestedData(),
1138
                     set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1139

    
1140
    self.assertEqual(q.Query(gqd),
1141
      [[(constants.RS_NORMAL, "default"),
1142
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1143
        (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1144
        (constants.RS_NORMAL, {}),
1145
        ],
1146
       [(constants.RS_NORMAL, "restricted"),
1147
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1148
        (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1149
                                                     self.custom_diskparams)),
1150
        (constants.RS_NORMAL, self.custom_diskparams),
1151
        ],
1152
       ])
1153

    
1154

    
1155
class TestOsQuery(unittest.TestCase):
1156
  def _Create(self, selected):
1157
    return query.Query(query.OS_FIELDS, selected)
1158

    
1159
  def test(self):
1160
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1161
    api_versions = [10, 0, 15, 5]
1162
    parameters = ["zpar3", "apar9"]
1163

    
1164
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1165
    assert (api_versions != sorted(api_versions) and
1166
            api_versions != utils.NiceSort(variants))
1167
    assert (parameters != sorted(parameters) and
1168
            parameters != utils.NiceSort(parameters))
1169

    
1170
    data = [
1171
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1172
                   variants=set(), api_versions=set(), parameters=set(),
1173
                   node_status={ "some": "status", }),
1174
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1175
                   variants=set(variants),
1176
                   api_versions=set(api_versions),
1177
                   parameters=set(parameters),
1178
                   node_status={ "some": "other", "status": None, }),
1179
      ]
1180

    
1181

    
1182
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1183
                      "api_versions", "parameters", "node_status"])
1184
    self.assertEqual(q.RequestedData(), set([]))
1185
    self.assertEqual(q.Query(data),
1186
                     [[(constants.RS_NORMAL, "debian"),
1187
                       (constants.RS_NORMAL, False),
1188
                       (constants.RS_NORMAL, False),
1189
                       (constants.RS_NORMAL, False),
1190
                       (constants.RS_NORMAL, []),
1191
                       (constants.RS_NORMAL, []),
1192
                       (constants.RS_NORMAL, []),
1193
                       (constants.RS_NORMAL, {"some": "status"})],
1194
                      [(constants.RS_NORMAL, "dos"),
1195
                       (constants.RS_NORMAL, True),
1196
                       (constants.RS_NORMAL, False),
1197
                       (constants.RS_NORMAL, True),
1198
                       (constants.RS_NORMAL,
1199
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1200
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1201
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1202
                       (constants.RS_NORMAL,
1203
                        { "some": "other", "status": None, })
1204
                       ]])
1205

    
1206

    
1207
class TestQueryFields(unittest.TestCase):
1208
  def testAllFields(self):
1209
    for fielddefs in query.ALL_FIELD_LISTS:
1210
      result = query.QueryFields(fielddefs, None)
1211
      self.assert_(isinstance(result, dict))
1212
      response = objects.QueryFieldsResponse.FromDict(result)
1213
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1214
        [(fdef2.name, fdef2.title)
1215
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1216
                                                key=lambda x: x[0].name)])
1217

    
1218
  def testSomeFields(self):
1219
    rnd = random.Random(5357)
1220

    
1221
    for _ in range(10):
1222
      for fielddefs in query.ALL_FIELD_LISTS:
1223
        if len(fielddefs) > 20:
1224
          sample_size = rnd.randint(5, 20)
1225
        else:
1226
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1227
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1228
                                                         sample_size)]
1229
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1230
        self.assert_(isinstance(result, dict))
1231
        response = objects.QueryFieldsResponse.FromDict(result)
1232
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1233
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1234

    
1235

    
1236
class TestQueryFilter(unittest.TestCase):
1237
  def testRequestedNames(self):
1238
    for (what, fielddefs) in query.ALL_FIELDS.items():
1239
      if what == constants.QR_JOB:
1240
        namefield = "id"
1241
        nameval = 123
1242
        namevalempty = 0
1243
        genval = lambda i: i * 10
1244
        randvals = [17361, 22015, 13193, 15215]
1245
      else:
1246
        nameval = "abc"
1247
        namevalempty = ""
1248
        genval = lambda i: "x%s" % i
1249
        randvals = ["x17361", "x22015", "x13193", "x15215"]
1250
        if what == constants.QR_EXPORT:
1251
          namefield = "export"
1252
        else:
1253
          namefield = "name"
1254

    
1255
      assert namefield in fielddefs
1256

    
1257
      reqnames = [genval(i) for i in range(4)]
1258
      innerfilter = [["=", namefield, v] for v in reqnames]
1259

    
1260
      # No name field
1261
      q = query.Query(fielddefs, [namefield],
1262
                      qfilter=["=", namefield, nameval], namefield=None)
1263
      self.assertEqual(q.RequestedNames(), None)
1264

    
1265
      # No filter
1266
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1267
      self.assertEqual(q.RequestedNames(), None)
1268

    
1269
      # Check empty query
1270
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1271
                      namefield=namefield)
1272
      self.assertEqual(q.RequestedNames(), None)
1273

    
1274
      # Check order
1275
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1276
                      namefield=namefield)
1277
      self.assertEqual(q.RequestedNames(), reqnames)
1278

    
1279
      # Check reverse order
1280
      q = query.Query(fielddefs, [namefield],
1281
                      qfilter=["|"] + list(reversed(innerfilter)),
1282
                      namefield=namefield)
1283
      self.assertEqual(q.RequestedNames(), list(reversed(reqnames)))
1284

    
1285
      # Duplicates
1286
      q = query.Query(fielddefs, [namefield],
1287
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1288
                      namefield=namefield)
1289
      self.assertEqual(q.RequestedNames(), reqnames)
1290

    
1291
      # Unknown name field
1292
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1293
                        namefield="_unknown_field_")
1294

    
1295
      # Filter with AND
1296
      q = query.Query(fielddefs, [namefield],
1297
                      qfilter=["|", ["=", namefield, nameval],
1298
                                    ["&", ["=", namefield, namevalempty]]],
1299
                      namefield=namefield)
1300
      self.assertTrue(q.RequestedNames() is None)
1301

    
1302
      # Filter with NOT
1303
      q = query.Query(fielddefs, [namefield],
1304
                      qfilter=["|", ["=", namefield, nameval],
1305
                                    ["!", ["=", namefield, namevalempty]]],
1306
                      namefield=namefield)
1307
      self.assertTrue(q.RequestedNames() is None)
1308

    
1309
      # Filter with only OR (names must be in correct order)
1310
      q = query.Query(fielddefs, [namefield],
1311
                      qfilter=["|", ["=", namefield, randvals[0]],
1312
                                    ["|", ["=", namefield, randvals[1]]],
1313
                                    ["|", ["|", ["=", namefield, randvals[2]]]],
1314
                                    ["=", namefield, randvals[3]]],
1315
                      namefield=namefield)
1316
      self.assertEqual(q.RequestedNames(), randvals)
1317

    
1318
  @staticmethod
1319
  def _GenNestedFilter(namefield, op, depth, nameval):
1320
    nested = ["=", namefield, nameval]
1321
    for i in range(depth):
1322
      nested = [op, nested]
1323
    return nested
1324

    
1325
  def testCompileFilter(self):
1326
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1327

    
1328
    for (what, fielddefs) in query.ALL_FIELDS.items():
1329
      if what == constants.QR_JOB:
1330
        namefield = "id"
1331
        nameval = 123
1332
      elif what == constants.QR_EXPORT:
1333
        namefield = "export"
1334
        nameval = "value"
1335
      else:
1336
        namefield = "name"
1337
        nameval = "value"
1338

    
1339
      checks = [
1340
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1341
        ["=", "_unknown_field", "value"],
1342
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1343
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1344
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1345
        ]
1346

    
1347
      for qfilter in checks:
1348
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1349
                          fielddefs, None, qfilter)
1350

    
1351
      for op in ["|", "!"]:
1352
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1353
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1354
                                                      qfilter)))
1355

    
1356
  def testQueryInputOrder(self):
1357
    fielddefs = query._PrepareFieldList([
1358
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1359
       None, 0, lambda ctx, item: item["pnode"]),
1360
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1361
       None, 0, lambda ctx, item: item["snode"]),
1362
      ], [])
1363

    
1364
    data = [
1365
      { "pnode": "node1", "snode": "node44", },
1366
      { "pnode": "node30", "snode": "node90", },
1367
      { "pnode": "node25", "snode": "node1", },
1368
      { "pnode": "node20", "snode": "node1", },
1369
      ]
1370

    
1371
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1372

    
1373
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1374
                    qfilter=qfilter)
1375
    self.assertTrue(q.RequestedNames() is None)
1376
    self.assertFalse(q.RequestedData())
1377
    self.assertEqual(q.Query(data),
1378
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1379
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1380
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1381

    
1382
    # Try again with reversed input data
1383
    self.assertEqual(q.Query(reversed(data)),
1384
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1385
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1386
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1387

    
1388
    # No name field, result must be in incoming order
1389
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1390
                    qfilter=qfilter)
1391
    self.assertFalse(q.RequestedData())
1392
    self.assertEqual(q.Query(data),
1393
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1394
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1395
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1396
    self.assertEqual(q.OldStyleQuery(data), [
1397
      ["node1", "node44"],
1398
      ["node25", "node1"],
1399
      ["node20", "node1"],
1400
      ])
1401
    self.assertEqual(q.Query(reversed(data)),
1402
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1403
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1404
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1405
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1406
      ["node20", "node1"],
1407
      ["node25", "node1"],
1408
      ["node1", "node44"],
1409
      ])
1410

    
1411
    # Name field, but no sorting, result must be in incoming order
1412
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1413
    self.assertFalse(q.RequestedData())
1414
    self.assertEqual(q.Query(data, sort_by_name=False),
1415
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1416
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1417
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1418
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1419
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1420
      ["node1", "node44"],
1421
      ["node30", "node90"],
1422
      ["node25", "node1"],
1423
      ["node20", "node1"],
1424
      ])
1425
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1426
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1427
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1428
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1429
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1430
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1431
      ["node20", "node1"],
1432
      ["node25", "node1"],
1433
      ["node30", "node90"],
1434
      ["node1", "node44"],
1435
      ])
1436

    
1437
  def testEqualNamesOrder(self):
1438
    fielddefs = query._PrepareFieldList([
1439
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1440
       None, 0, lambda ctx, item: item["pnode"]),
1441
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1442
       None, 0, lambda ctx, item: item["num"]),
1443
      ], [])
1444

    
1445
    data = [
1446
      { "pnode": "node1", "num": 100, },
1447
      { "pnode": "node1", "num": 25, },
1448
      { "pnode": "node2", "num": 90, },
1449
      { "pnode": "node2", "num": 30, },
1450
      ]
1451

    
1452
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1453
                    qfilter=["|", ["=", "pnode", "node1"],
1454
                                  ["=", "pnode", "node2"],
1455
                                  ["=", "pnode", "node1"]])
1456
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1457
                     msg="Did not return unique names")
1458
    self.assertFalse(q.RequestedData())
1459
    self.assertEqual(q.Query(data),
1460
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1461
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1462
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1463
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1464
    self.assertEqual(q.Query(data, sort_by_name=False),
1465
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1466
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1467
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1468
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1469

    
1470
    data = [
1471
      { "pnode": "nodeX", "num": 50, },
1472
      { "pnode": "nodeY", "num": 40, },
1473
      { "pnode": "nodeX", "num": 30, },
1474
      { "pnode": "nodeX", "num": 20, },
1475
      { "pnode": "nodeM", "num": 10, },
1476
      ]
1477

    
1478
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1479
                    qfilter=["|", ["=", "pnode", "nodeX"],
1480
                                  ["=", "pnode", "nodeY"],
1481
                                  ["=", "pnode", "nodeY"],
1482
                                  ["=", "pnode", "nodeY"],
1483
                                  ["=", "pnode", "nodeM"]])
1484
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1485
                     msg="Did not return unique names")
1486
    self.assertFalse(q.RequestedData())
1487

    
1488
    # First sorted by name, then input order
1489
    self.assertEqual(q.Query(data, sort_by_name=True),
1490
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1491
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1492
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1493
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1494
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1495

    
1496
    # Input order
1497
    self.assertEqual(q.Query(data, sort_by_name=False),
1498
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1499
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1500
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1501
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1502
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1503

    
1504
  def testFilter(self):
1505
    (DK_A, DK_B) = range(1000, 1002)
1506

    
1507
    fielddefs = query._PrepareFieldList([
1508
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1509
       DK_A, 0, lambda ctx, item: item["name"]),
1510
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1511
       DK_B, 0, lambda ctx, item: item["other"]),
1512
      ], [])
1513

    
1514
    data = [
1515
      { "name": "node1", "other": "foo", },
1516
      { "name": "node2", "other": "bar", },
1517
      { "name": "node3", "other": "Hello", },
1518
      ]
1519

    
1520
    # Empty filter
1521
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1522
                    qfilter=["|"])
1523
    self.assertTrue(q.RequestedNames() is None)
1524
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1525
    self.assertEqual(q.Query(data), [])
1526

    
1527
    # Normal filter
1528
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1529
                    qfilter=["=", "name", "node1"])
1530
    self.assertEqual(q.RequestedNames(), ["node1"])
1531
    self.assertEqual(q.Query(data),
1532
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1533

    
1534
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1535
                    qfilter=(["|", ["=", "name", "node1"],
1536
                                   ["=", "name", "node3"]]))
1537
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1538
    self.assertEqual(q.Query(data),
1539
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1540
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1541

    
1542
    # Complex filter
1543
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1544
                    qfilter=(["|", ["=", "name", "node1"],
1545
                                   ["|", ["=", "name", "node3"],
1546
                                         ["=", "name", "node2"]],
1547
                                   ["=", "name", "node3"]]))
1548
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1549
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1550
    self.assertEqual(q.Query(data),
1551
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1552
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1553
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1554

    
1555
    # Filter data type mismatch
1556
    for i in [-1, 0, 1, 123, [], None, True, False]:
1557
      self.assertRaises(errors.ParameterError, query.Query,
1558
                        fielddefs, ["name", "other"], namefield="name",
1559
                        qfilter=["=", "name", i])
1560

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

    
1569
    # Not equal
1570
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1571
                    qfilter=["!=", "name", "node3"])
1572
    self.assertTrue(q.RequestedNames() is None)
1573
    self.assertEqual(q.Query(data),
1574
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1575
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1576

    
1577
    # Data type
1578
    q = query.Query(fielddefs, [], namefield="name",
1579
                    qfilter=["|", ["=", "other", "bar"],
1580
                                  ["=", "name", "foo"]])
1581
    self.assertTrue(q.RequestedNames() is None)
1582
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1583
    self.assertEqual(q.Query(data), [[]])
1584

    
1585
    # Only one data type
1586
    q = query.Query(fielddefs, ["other"], namefield="name",
1587
                    qfilter=["=", "other", "bar"])
1588
    self.assertTrue(q.RequestedNames() is None)
1589
    self.assertEqual(q.RequestedData(), set([DK_B]))
1590
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1591

    
1592
    q = query.Query(fielddefs, [], namefield="name",
1593
                    qfilter=["=", "other", "bar"])
1594
    self.assertTrue(q.RequestedNames() is None)
1595
    self.assertEqual(q.RequestedData(), set([DK_B]))
1596
    self.assertEqual(q.Query(data), [[]])
1597

    
1598
    # Data type in boolean operator
1599
    q = query.Query(fielddefs, [], namefield="name",
1600
                    qfilter=["?", "name"])
1601
    self.assertTrue(q.RequestedNames() is None)
1602
    self.assertEqual(q.RequestedData(), set([DK_A]))
1603
    self.assertEqual(q.Query(data), [[], [], []])
1604

    
1605
    q = query.Query(fielddefs, [], namefield="name",
1606
                    qfilter=["!", ["?", "name"]])
1607
    self.assertTrue(q.RequestedNames() is None)
1608
    self.assertEqual(q.RequestedData(), set([DK_A]))
1609
    self.assertEqual(q.Query(data), [])
1610

    
1611
  def testFilterContains(self):
1612
    fielddefs = query._PrepareFieldList([
1613
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1614
       None, 0, lambda ctx, item: item["name"]),
1615
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1616
       None, 0, lambda ctx, item: item["other"]),
1617
      ], [])
1618

    
1619
    data = [
1620
      { "name": "node2", "other": ["x", "y", "bar"], },
1621
      { "name": "node3", "other": "Hello", },
1622
      { "name": "node1", "other": ["a", "b", "foo"], },
1623
      { "name": "empty", "other": []},
1624
      ]
1625

    
1626
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1627
                    qfilter=["=[]", "other", "bar"])
1628
    self.assertTrue(q.RequestedNames() is None)
1629
    self.assertEqual(q.Query(data), [
1630
      [(constants.RS_NORMAL, "node2"),
1631
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1632
      ])
1633

    
1634
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1635
                    qfilter=["|", ["=[]", "other", "bar"],
1636
                                  ["=[]", "other", "a"],
1637
                                  ["=[]", "other", "b"]])
1638
    self.assertTrue(q.RequestedNames() is None)
1639
    self.assertEqual(q.Query(data), [
1640
      [(constants.RS_NORMAL, "node1"),
1641
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1642
      [(constants.RS_NORMAL, "node2"),
1643
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1644
      ])
1645
    self.assertEqual(q.OldStyleQuery(data), [
1646
      ["node1", ["a", "b", "foo"]],
1647
      ["node2", ["x", "y", "bar"]],
1648
      ])
1649

    
1650
    # Boolean test
1651
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1652
                    qfilter=["?", "other"])
1653
    self.assertEqual(q.OldStyleQuery(data), [
1654
      ["node1", ["a", "b", "foo"]],
1655
      ["node2", ["x", "y", "bar"]],
1656
      ["node3", "Hello"],
1657
      ])
1658

    
1659
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1660
                    qfilter=["!", ["?", "other"]])
1661
    self.assertEqual(q.OldStyleQuery(data), [
1662
      ["empty", []],
1663
      ])
1664

    
1665
  def testFilterHostname(self):
1666
    fielddefs = query._PrepareFieldList([
1667
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1668
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1669
      ], [])
1670

    
1671
    data = [
1672
      { "name": "node1.example.com", },
1673
      { "name": "node2.example.com", },
1674
      { "name": "node2.example.net", },
1675
      ]
1676

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

    
1685
    q = query.Query(fielddefs, ["name"], namefield="name",
1686
                    qfilter=["=", "name", "node1"])
1687
    self.assertEqual(q.RequestedNames(), ["node1"])
1688
    self.assertEqual(q.Query(data), [
1689
      [(constants.RS_NORMAL, "node1.example.com")],
1690
      ])
1691

    
1692
    q = query.Query(fielddefs, ["name"], namefield="name",
1693
                    qfilter=["=", "name", "othername"])
1694
    self.assertEqual(q.RequestedNames(), ["othername"])
1695
    self.assertEqual(q.Query(data), [])
1696

    
1697
    q = query.Query(fielddefs, ["name"], namefield="name",
1698
                    qfilter=["|", ["=", "name", "node1.example.com"],
1699
                                  ["=", "name", "node2"]])
1700
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1701
    self.assertEqual(q.Query(data), [
1702
      [(constants.RS_NORMAL, "node1.example.com")],
1703
      [(constants.RS_NORMAL, "node2.example.com")],
1704
      [(constants.RS_NORMAL, "node2.example.net")],
1705
      ])
1706
    self.assertEqual(q.OldStyleQuery(data), [
1707
      ["node1.example.com"],
1708
      ["node2.example.com"],
1709
      ["node2.example.net"],
1710
      ])
1711

    
1712
    q = query.Query(fielddefs, ["name"], namefield="name",
1713
                    qfilter=["!=", "name", "node1"])
1714
    self.assertTrue(q.RequestedNames() is None)
1715
    self.assertEqual(q.Query(data), [
1716
      [(constants.RS_NORMAL, "node2.example.com")],
1717
      [(constants.RS_NORMAL, "node2.example.net")],
1718
      ])
1719
    self.assertEqual(q.OldStyleQuery(data), [
1720
      ["node2.example.com"],
1721
      ["node2.example.net"],
1722
      ])
1723

    
1724
  def testFilterBoolean(self):
1725
    fielddefs = query._PrepareFieldList([
1726
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1727
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1728
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1729
       None, 0, lambda ctx, item: item["value"]),
1730
      ], [])
1731

    
1732
    data = [
1733
      { "name": "node1", "value": False, },
1734
      { "name": "node2", "value": True, },
1735
      { "name": "node3", "value": True, },
1736
      ]
1737

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

    
1748
    q = query.Query(fielddefs, ["name", "value"],
1749
                    qfilter=["|", ["=", "value", False],
1750
                                  ["!", ["=", "value", False]]])
1751
    self.assertTrue(q.RequestedNames() is None)
1752
    self.assertEqual(q.Query(data), [
1753
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1754
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1755
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1756
      ])
1757

    
1758
    # Comparing bool with string
1759
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1760
      self.assertRaises(errors.ParameterError, query.Query,
1761
                        fielddefs, ["name", "value"],
1762
                        qfilter=["=", "value", i])
1763

    
1764
    # Truth filter
1765
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1766
    self.assertTrue(q.RequestedNames() is None)
1767
    self.assertEqual(q.Query(data), [
1768
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1769
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1770
      ])
1771

    
1772
    # Negative bool filter
1773
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1774
    self.assertTrue(q.RequestedNames() is None)
1775
    self.assertEqual(q.Query(data), [
1776
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1777
      ])
1778

    
1779
    # Complex truth filter
1780
    q = query.Query(fielddefs, ["name", "value"],
1781
                    qfilter=["|", ["&", ["=", "name", "node1"],
1782
                                        ["!", ["?", "value"]]],
1783
                                  ["?", "value"]])
1784
    self.assertTrue(q.RequestedNames() is None)
1785
    self.assertEqual(q.Query(data), [
1786
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1787
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1788
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1789
      ])
1790

    
1791
  def testFilterRegex(self):
1792
    fielddefs = query._PrepareFieldList([
1793
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1794
       None, 0, lambda ctx, item: item["name"]),
1795
      ], [])
1796

    
1797
    data = [
1798
      { "name": "node1.example.com", },
1799
      { "name": "node2.site.example.com", },
1800
      { "name": "node2.example.net", },
1801

    
1802
      # Empty name
1803
      { "name": "", },
1804
      ]
1805

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

    
1813
    q = query.Query(fielddefs, ["name"], namefield="name",
1814
                    qfilter=["=~", "name", "^node2"])
1815
    self.assertTrue(q.RequestedNames() is None)
1816
    self.assertEqual(q.Query(data), [
1817
      [(constants.RS_NORMAL, "node2.example.net")],
1818
      [(constants.RS_NORMAL, "node2.site.example.com")],
1819
      ])
1820

    
1821
    q = query.Query(fielddefs, ["name"], namefield="name",
1822
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1823
    self.assertTrue(q.RequestedNames() is None)
1824
    self.assertEqual(q.Query(data), [
1825
      [(constants.RS_NORMAL, "node1.example.com")],
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, "node1.example.com")],
1834
      [(constants.RS_NORMAL, "node2.example.net")],
1835
      [(constants.RS_NORMAL, "node2.site.example.com")],
1836
      ])
1837

    
1838
    q = query.Query(fielddefs, ["name"], namefield="name",
1839
                    qfilter=["=~", "name", r"^$"])
1840
    self.assertTrue(q.RequestedNames() is None)
1841
    self.assertEqual(q.Query(data), [
1842
      [(constants.RS_NORMAL, "")],
1843
      ])
1844

    
1845
    # Invalid regular expression
1846
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1847
                      qfilter=["=~", "name", r"["])
1848

    
1849
  def testFilterLessGreater(self):
1850
    fielddefs = query._PrepareFieldList([
1851
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1852
       None, 0, lambda ctx, item: item),
1853
      ], [])
1854

    
1855
    data = range(100)
1856

    
1857
    q = query.Query(fielddefs, ["value"],
1858
                    qfilter=["<", "value", 20])
1859
    self.assertTrue(q.RequestedNames() is None)
1860
    self.assertEqual(q.Query(data),
1861
                     [[(constants.RS_NORMAL, i)] for i in range(20)])
1862

    
1863
    q = query.Query(fielddefs, ["value"],
1864
                    qfilter=["<=", "value", 30])
1865
    self.assertTrue(q.RequestedNames() is None)
1866
    self.assertEqual(q.Query(data),
1867
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1868

    
1869
    q = query.Query(fielddefs, ["value"],
1870
                    qfilter=[">", "value", 40])
1871
    self.assertTrue(q.RequestedNames() is None)
1872
    self.assertEqual(q.Query(data),
1873
                     [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
1874

    
1875
    q = query.Query(fielddefs, ["value"],
1876
                    qfilter=[">=", "value", 50])
1877
    self.assertTrue(q.RequestedNames() is None)
1878
    self.assertEqual(q.Query(data),
1879
                     [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
1880

    
1881
  def testFilterLessGreaterJobId(self):
1882
    fielddefs = query._PrepareFieldList([
1883
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1884
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1885
      ], [])
1886

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

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

    
1891
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1892
    self.assertTrue(q.RequestedNames() is None)
1893
    self.assertEqual(q.Query(data), [
1894
      [(constants.RS_NORMAL, "1")],
1895
      [(constants.RS_NORMAL, "2")],
1896
      [(constants.RS_NORMAL, "3")],
1897
      [(constants.RS_NORMAL, "10")],
1898
      [(constants.RS_NORMAL, "15")],
1899
      [(constants.RS_NORMAL, "7")],
1900
      ])
1901

    
1902
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1903
    self.assertTrue(q.RequestedNames() is None)
1904
    self.assertEqual(q.Query(data), [
1905
      [(constants.RS_NORMAL, "102")],
1906
      [(constants.RS_NORMAL, "120")],
1907
      [(constants.RS_NORMAL, "125")],
1908
      [(constants.RS_NORMAL, "100")],
1909
      ])
1910

    
1911
    # Integers are no valid job IDs
1912
    self.assertRaises(errors.ParameterError, query.Query,
1913
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1914

    
1915
  def testFilterLessGreaterSplitTimestamp(self):
1916
    fielddefs = query._PrepareFieldList([
1917
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1918
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1919
      ], [])
1920

    
1921
    data = [
1922
      utils.SplitTime(0),
1923
      utils.SplitTime(0.1),
1924
      utils.SplitTime(18224.7872),
1925
      utils.SplitTime(919896.12623),
1926
      utils.SplitTime(999),
1927
      utils.SplitTime(989.9999),
1928
      ]
1929

    
1930
    for i in [0, [0, 0]]:
1931
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1932
      self.assertTrue(q.RequestedNames() is None)
1933
      self.assertEqual(q.Query(data), [])
1934

    
1935
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1936
    self.assertTrue(q.RequestedNames() is None)
1937
    self.assertEqual(q.Query(data), [
1938
      [(constants.RS_NORMAL, (0, 0))],
1939
      [(constants.RS_NORMAL, (0, 100000))],
1940
      [(constants.RS_NORMAL, (999, 0))],
1941
      [(constants.RS_NORMAL, (989, 999900))],
1942
      ])
1943

    
1944
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
1945
    self.assertTrue(q.RequestedNames() is None)
1946
    self.assertEqual(q.Query(data), [
1947
      [(constants.RS_NORMAL, (18224, 787200))],
1948
      [(constants.RS_NORMAL, (919896, 126230))],
1949
      ])
1950

    
1951
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1952
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1953
      self.assertTrue(q.RequestedNames() is None)
1954
      self.assertEqual(q.Query(data), [
1955
        [(constants.RS_NORMAL, (18224, 787200))],
1956
        [(constants.RS_NORMAL, (919896, 126230))],
1957
        ])
1958

    
1959
    q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880])
1960
    self.assertTrue(q.RequestedNames() is None)
1961
    self.assertEqual(q.Query(data), [
1962
      [(constants.RS_NORMAL, (919896, 126230))],
1963
      ])
1964

    
1965

    
1966
if __name__ == "__main__":
1967
  testutils.GanetiTestProgram()