Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.query_unittest.py @ 1ec34e26

History | View | Annotate | Download (74.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

    
38
import testutils
39

    
40

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

    
46

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

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

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

    
57

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

    
65

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
298

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

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

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

    
324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
481
    master_node.group = ng_uuid
482

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
607

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

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

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

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

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

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

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

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

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

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

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

    
814
    assert not utils.FindDuplicates(inst.uuid for inst in instances)
815
    assert not utils.FindDuplicates(inst.name for inst in instances)
816

    
817
    instbyname = dict((inst.name, inst) for inst in instances)
818

    
819
    disk_usage = dict((inst.uuid,
820
                       gmi.ComputeDiskSize(inst.disk_template,
821
                                           [{"size": disk.size}
822
                                           for disk in inst.disks]))
823
                      for inst in instances)
824

    
825
    inst_bridges = {
826
      "inst3-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
827
      "inst4-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
828
                     None, "eth123"],
829
      }
830

    
831
    live_data = {
832
      "inst2-uuid": {
833
        "vcpus": 3,
834
        },
835
      "inst4-uuid": {
836
        "memory": 123,
837
        },
838
      "inst6-uuid": {
839
        "memory": 768,
840
        },
841
      "inst7-uuid": {
842
        "vcpus": 3,
843
        },
844
      }
845
    wrongnode_inst = set(["inst7-uuid"])
846

    
847
    consinfo = dict((inst.uuid, None) for inst in instances)
848
    consinfo["inst7-uuid"] = \
849
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
850
                              host=instbyname["inst7"].primary_node,
851
                              user="root",
852
                              command=["hostname"]).ToDict()
853

    
854
    nodes = dict([(uuid, objects.Node(
855
                           name="%s.example.com" % uuid,
856
                           uuid=uuid,
857
                           group="default-uuid"))
858
                  for uuid in node_uuids])
859

    
860
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
861
                                  offline_nodes, bad_nodes, live_data,
862
                                  wrongnode_inst, consinfo, nodes, {}, {})
863
    result = q.Query(iqd)
864
    self.assertEqual(len(result), len(instances))
865
    self.assert_(compat.all(len(row) == len(selected)
866
                            for row in result))
867

    
868
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
869
           "Offline nodes not included in bad nodes"
870

    
871
    tested_status = set()
872

    
873
    for (inst, row) in zip(instances, result):
874
      assert inst.primary_node in node_uuids
875

    
876
      self.assertEqual(row[fieldidx["name"]],
877
                       (constants.RS_NORMAL, inst.name))
878

    
879
      if inst.primary_node in offline_nodes:
880
        exp_status = constants.INSTST_NODEOFFLINE
881
      elif inst.primary_node in bad_nodes:
882
        exp_status = constants.INSTST_NODEDOWN
883
      elif inst.uuid in live_data:
884
        if inst.uuid in wrongnode_inst:
885
          exp_status = constants.INSTST_WRONGNODE
886
        elif inst.admin_state == constants.ADMINST_UP:
887
          exp_status = constants.INSTST_RUNNING
888
        else:
889
          exp_status = constants.INSTST_ERRORUP
890
      elif inst.admin_state == constants.ADMINST_UP:
891
        exp_status = constants.INSTST_ERRORDOWN
892
      elif inst.admin_state == constants.ADMINST_DOWN:
893
        exp_status = constants.INSTST_ADMINDOWN
894
      else:
895
        exp_status = constants.INSTST_ADMINOFFLINE
896

    
897
      self.assertEqual(row[fieldidx["status"]],
898
                       (constants.RS_NORMAL, exp_status))
899

    
900
      (_, status) = row[fieldidx["status"]]
901
      tested_status.add(status)
902

    
903
      #FIXME(dynmem): check oper_ram vs min/max mem
904
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
905
        if inst.primary_node in bad_nodes:
906
          exp = (constants.RS_NODATA, None)
907
        elif inst.uuid in live_data:
908
          value = live_data[inst.uuid].get(livefield, None)
909
          if value is None:
910
            exp = (constants.RS_UNAVAIL, None)
911
          else:
912
            exp = (constants.RS_NORMAL, value)
913
        else:
914
          exp = (constants.RS_UNAVAIL, None)
915

    
916
        self.assertEqual(row[fieldidx[field]], exp)
917

    
918
      bridges = inst_bridges.get(inst.uuid, [])
919
      self.assertEqual(row[fieldidx["nic.bridges"]],
920
                       (constants.RS_NORMAL, bridges))
921
      if bridges:
922
        self.assertEqual(row[fieldidx["bridge"]],
923
                         (constants.RS_NORMAL, bridges[0]))
924
      else:
925
        self.assertEqual(row[fieldidx["bridge"]],
926
                         (constants.RS_UNAVAIL, None))
927

    
928
      for i in range(constants.MAX_NICS):
929
        if i < len(bridges) and bridges[i] is not None:
930
          exp = (constants.RS_NORMAL, bridges[i])
931
        else:
932
          exp = (constants.RS_UNAVAIL, None)
933
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
934

    
935
      if inst.primary_node in bad_nodes:
936
        exp = (constants.RS_NODATA, None)
937
      else:
938
        exp = (constants.RS_NORMAL, inst.uuid in live_data)
939
      self.assertEqual(row[fieldidx["oper_state"]], exp)
940

    
941
      cust_exp = (constants.RS_NORMAL, {})
942
      if inst.os == "deb99":
943
        if inst.uuid == "inst6-uuid":
944
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
945
          cust_exp = exp
946
        else:
947
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
948
      else:
949
        exp = (constants.RS_NORMAL, {})
950
      self.assertEqual(row[fieldidx["osparams"]], exp)
951
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
952

    
953
      usage = disk_usage[inst.uuid]
954
      if usage is None:
955
        usage = 0
956
      self.assertEqual(row[fieldidx["disk_usage"]],
957
                       (constants.RS_NORMAL, usage))
958

    
959
      for alias, target in [("sda_size", "disk.size/0"),
960
                            ("sdb_size", "disk.size/1"),
961
                            ("vcpus", "be/vcpus"),
962
                            ("ip", "nic.ip/0"),
963
                            ("mac", "nic.mac/0"),
964
                            ("bridge", "nic.bridge/0"),
965
                            ("nic_mode", "nic.mode/0"),
966
                            ("nic_link", "nic.link/0"),
967
                            ]:
968
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
969

    
970
      for field in ["ctime", "mtime"]:
971
        if getattr(inst, field) is None:
972
          # No ctime/mtime
973
          exp = (constants.RS_UNAVAIL, None)
974
        else:
975
          exp = (constants.RS_NORMAL, getattr(inst, field))
976
        self.assertEqual(row[fieldidx[field]], exp)
977

    
978
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
979

    
980
    # Ensure all possible status' have been tested
981
    self.assertEqual(tested_status, constants.INSTST_ALL)
982

    
983
  def _CheckInstanceConsole(self, instance, (status, consdata)):
984
    if instance.name == "inst7":
985
      self.assertEqual(status, constants.RS_NORMAL)
986
      console = objects.InstanceConsole.FromDict(consdata)
987
      self.assertTrue(console.Validate())
988
      self.assertEqual(console.host, instance.primary_node)
989
    else:
990
      self.assertEqual(status, constants.RS_UNAVAIL)
991

    
992

    
993
class TestGroupQuery(unittest.TestCase):
994

    
995
  def setUp(self):
996
    self.custom_diskparams = {
997
      constants.DT_DRBD8: {
998
        constants.DRBD_DEFAULT_METAVG: "foobar",
999
      },
1000
    }
1001

    
1002
    self.groups = [
1003
      objects.NodeGroup(name="default",
1004
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
1005
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED,
1006
                        ipolicy=objects.MakeEmptyIPolicy(),
1007
                        ndparams={},
1008
                        diskparams={},
1009
                        ),
1010
      objects.NodeGroup(name="restricted",
1011
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
1012
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
1013
                        ipolicy=objects.MakeEmptyIPolicy(),
1014
                        ndparams={},
1015
                        diskparams=self.custom_diskparams,
1016
                        ),
1017
      ]
1018
    self.cluster = objects.Cluster(cluster_name="testcluster",
1019
      hvparams=constants.HVC_DEFAULTS,
1020
      beparams={
1021
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
1022
        },
1023
      nicparams={
1024
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
1025
        },
1026
      ndparams=constants.NDC_DEFAULTS,
1027
      ipolicy=constants.IPOLICY_DEFAULTS,
1028
      diskparams=constants.DISK_DT_DEFAULTS,
1029
      )
1030

    
1031
  def _Create(self, selected):
1032
    return query.Query(query.GROUP_FIELDS, selected)
1033

    
1034
  def testSimple(self):
1035
    q = self._Create(["name", "uuid", "alloc_policy"])
1036
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1037

    
1038
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1039

    
1040
    self.assertEqual(q.Query(gqd),
1041
      [[(constants.RS_NORMAL, "default"),
1042
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1043
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1044
        ],
1045
       [(constants.RS_NORMAL, "restricted"),
1046
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1047
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1048
        ],
1049
       ])
1050

    
1051
  def testNodes(self):
1052
    groups_to_nodes = {
1053
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1054
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1055
      }
1056

    
1057
    q = self._Create(["name", "node_cnt", "node_list"])
1058
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1059
                               False)
1060

    
1061
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1062

    
1063
    self.assertEqual(q.Query(gqd),
1064
                     [[(constants.RS_NORMAL, "default"),
1065
                       (constants.RS_NORMAL, 2),
1066
                       (constants.RS_NORMAL, ["node1", "node2"]),
1067
                       ],
1068
                      [(constants.RS_NORMAL, "restricted"),
1069
                       (constants.RS_NORMAL, 3),
1070
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1071
                       ],
1072
                      ])
1073

    
1074
  def testInstances(self):
1075
    groups_to_instances = {
1076
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1077
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1078
      }
1079

    
1080
    q = self._Create(["pinst_cnt", "pinst_list"])
1081
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1082
      groups_to_instances, False)
1083

    
1084
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1085

    
1086
    self.assertEqual(q.Query(gqd),
1087
                     [[(constants.RS_NORMAL, 2),
1088
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1089
                       ],
1090
                      [(constants.RS_NORMAL, 3),
1091
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1092
                       ],
1093
                      ])
1094

    
1095
  def testDiskparams(self):
1096
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1097
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1098

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

    
1102
    self.assertEqual(q.Query(gqd),
1103
      [[(constants.RS_NORMAL, "default"),
1104
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1105
        (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1106
        (constants.RS_NORMAL, {}),
1107
        ],
1108
       [(constants.RS_NORMAL, "restricted"),
1109
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1110
        (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1111
                                                     self.custom_diskparams)),
1112
        (constants.RS_NORMAL, self.custom_diskparams),
1113
        ],
1114
       ])
1115

    
1116

    
1117
class TestOsQuery(unittest.TestCase):
1118
  def _Create(self, selected):
1119
    return query.Query(query.OS_FIELDS, selected)
1120

    
1121
  def test(self):
1122
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1123
    api_versions = [10, 0, 15, 5]
1124
    parameters = ["zpar3", "apar9"]
1125

    
1126
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1127
    assert (api_versions != sorted(api_versions) and
1128
            api_versions != utils.NiceSort(variants))
1129
    assert (parameters != sorted(parameters) and
1130
            parameters != utils.NiceSort(parameters))
1131

    
1132
    data = [
1133
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1134
                   variants=set(), api_versions=set(), parameters=set(),
1135
                   node_status={ "some": "status", }),
1136
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1137
                   variants=set(variants),
1138
                   api_versions=set(api_versions),
1139
                   parameters=set(parameters),
1140
                   node_status={ "some": "other", "status": None, }),
1141
      ]
1142

    
1143

    
1144
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1145
                      "api_versions", "parameters", "node_status"])
1146
    self.assertEqual(q.RequestedData(), set([]))
1147
    self.assertEqual(q.Query(data),
1148
                     [[(constants.RS_NORMAL, "debian"),
1149
                       (constants.RS_NORMAL, False),
1150
                       (constants.RS_NORMAL, False),
1151
                       (constants.RS_NORMAL, False),
1152
                       (constants.RS_NORMAL, []),
1153
                       (constants.RS_NORMAL, []),
1154
                       (constants.RS_NORMAL, []),
1155
                       (constants.RS_NORMAL, {"some": "status"})],
1156
                      [(constants.RS_NORMAL, "dos"),
1157
                       (constants.RS_NORMAL, True),
1158
                       (constants.RS_NORMAL, False),
1159
                       (constants.RS_NORMAL, True),
1160
                       (constants.RS_NORMAL,
1161
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1162
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1163
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1164
                       (constants.RS_NORMAL,
1165
                        { "some": "other", "status": None, })
1166
                       ]])
1167

    
1168

    
1169
class TestQueryFields(unittest.TestCase):
1170
  def testAllFields(self):
1171
    for fielddefs in query.ALL_FIELD_LISTS:
1172
      result = query.QueryFields(fielddefs, None)
1173
      self.assert_(isinstance(result, dict))
1174
      response = objects.QueryFieldsResponse.FromDict(result)
1175
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1176
        [(fdef2.name, fdef2.title)
1177
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1178
                                                key=lambda x: x[0].name)])
1179

    
1180
  def testSomeFields(self):
1181
    rnd = random.Random(5357)
1182

    
1183
    for _ in range(10):
1184
      for fielddefs in query.ALL_FIELD_LISTS:
1185
        if len(fielddefs) > 20:
1186
          sample_size = rnd.randint(5, 20)
1187
        else:
1188
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1189
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1190
                                                         sample_size)]
1191
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1192
        self.assert_(isinstance(result, dict))
1193
        response = objects.QueryFieldsResponse.FromDict(result)
1194
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1195
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1196

    
1197

    
1198
class TestQueryFilter(unittest.TestCase):
1199
  def testRequestedNames(self):
1200
    for (what, fielddefs) in query.ALL_FIELDS.items():
1201
      if what == constants.QR_JOB:
1202
        namefield = "id"
1203
        nameval = 123
1204
        namevalempty = 0
1205
        genval = lambda i: i * 10
1206
        randvals = [17361, 22015, 13193, 15215]
1207
      else:
1208
        nameval = "abc"
1209
        namevalempty = ""
1210
        genval = lambda i: "x%s" % i
1211
        randvals = ["x17361", "x22015", "x13193", "x15215"]
1212
        if what == constants.QR_EXPORT:
1213
          namefield = "export"
1214
        else:
1215
          namefield = "name"
1216

    
1217
      assert namefield in fielddefs
1218

    
1219
      reqnames = [genval(i) for i in range(4)]
1220
      innerfilter = [["=", namefield, v] for v in reqnames]
1221

    
1222
      # No name field
1223
      q = query.Query(fielddefs, [namefield],
1224
                      qfilter=["=", namefield, nameval], namefield=None)
1225
      self.assertEqual(q.RequestedNames(), None)
1226

    
1227
      # No filter
1228
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1229
      self.assertEqual(q.RequestedNames(), None)
1230

    
1231
      # Check empty query
1232
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1233
                      namefield=namefield)
1234
      self.assertEqual(q.RequestedNames(), None)
1235

    
1236
      # Check order
1237
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1238
                      namefield=namefield)
1239
      self.assertEqual(q.RequestedNames(), reqnames)
1240

    
1241
      # Check reverse order
1242
      q = query.Query(fielddefs, [namefield],
1243
                      qfilter=["|"] + list(reversed(innerfilter)),
1244
                      namefield=namefield)
1245
      self.assertEqual(q.RequestedNames(), list(reversed(reqnames)))
1246

    
1247
      # Duplicates
1248
      q = query.Query(fielddefs, [namefield],
1249
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1250
                      namefield=namefield)
1251
      self.assertEqual(q.RequestedNames(), reqnames)
1252

    
1253
      # Unknown name field
1254
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1255
                        namefield="_unknown_field_")
1256

    
1257
      # Filter with AND
1258
      q = query.Query(fielddefs, [namefield],
1259
                      qfilter=["|", ["=", namefield, nameval],
1260
                                    ["&", ["=", namefield, namevalempty]]],
1261
                      namefield=namefield)
1262
      self.assertTrue(q.RequestedNames() is None)
1263

    
1264
      # Filter with NOT
1265
      q = query.Query(fielddefs, [namefield],
1266
                      qfilter=["|", ["=", namefield, nameval],
1267
                                    ["!", ["=", namefield, namevalempty]]],
1268
                      namefield=namefield)
1269
      self.assertTrue(q.RequestedNames() is None)
1270

    
1271
      # Filter with only OR (names must be in correct order)
1272
      q = query.Query(fielddefs, [namefield],
1273
                      qfilter=["|", ["=", namefield, randvals[0]],
1274
                                    ["|", ["=", namefield, randvals[1]]],
1275
                                    ["|", ["|", ["=", namefield, randvals[2]]]],
1276
                                    ["=", namefield, randvals[3]]],
1277
                      namefield=namefield)
1278
      self.assertEqual(q.RequestedNames(), randvals)
1279

    
1280
  @staticmethod
1281
  def _GenNestedFilter(namefield, op, depth, nameval):
1282
    nested = ["=", namefield, nameval]
1283
    for i in range(depth):
1284
      nested = [op, nested]
1285
    return nested
1286

    
1287
  def testCompileFilter(self):
1288
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1289

    
1290
    for (what, fielddefs) in query.ALL_FIELDS.items():
1291
      if what == constants.QR_JOB:
1292
        namefield = "id"
1293
        nameval = 123
1294
      elif what == constants.QR_EXPORT:
1295
        namefield = "export"
1296
        nameval = "value"
1297
      else:
1298
        namefield = "name"
1299
        nameval = "value"
1300

    
1301
      checks = [
1302
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1303
        ["=", "_unknown_field", "value"],
1304
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1305
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1306
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1307
        ]
1308

    
1309
      for qfilter in checks:
1310
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1311
                          fielddefs, None, qfilter)
1312

    
1313
      for op in ["|", "!"]:
1314
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1315
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1316
                                                      qfilter)))
1317

    
1318
  def testQueryInputOrder(self):
1319
    fielddefs = query._PrepareFieldList([
1320
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1321
       None, 0, lambda ctx, item: item["pnode"]),
1322
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1323
       None, 0, lambda ctx, item: item["snode"]),
1324
      ], [])
1325

    
1326
    data = [
1327
      { "pnode": "node1", "snode": "node44", },
1328
      { "pnode": "node30", "snode": "node90", },
1329
      { "pnode": "node25", "snode": "node1", },
1330
      { "pnode": "node20", "snode": "node1", },
1331
      ]
1332

    
1333
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1334

    
1335
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1336
                    qfilter=qfilter)
1337
    self.assertTrue(q.RequestedNames() is None)
1338
    self.assertFalse(q.RequestedData())
1339
    self.assertEqual(q.Query(data),
1340
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1341
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1342
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1343

    
1344
    # Try again with reversed input data
1345
    self.assertEqual(q.Query(reversed(data)),
1346
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1347
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1348
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1349

    
1350
    # No name field, result must be in incoming order
1351
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1352
                    qfilter=qfilter)
1353
    self.assertFalse(q.RequestedData())
1354
    self.assertEqual(q.Query(data),
1355
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1356
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1357
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1358
    self.assertEqual(q.OldStyleQuery(data), [
1359
      ["node1", "node44"],
1360
      ["node25", "node1"],
1361
      ["node20", "node1"],
1362
      ])
1363
    self.assertEqual(q.Query(reversed(data)),
1364
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1365
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1366
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1367
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1368
      ["node20", "node1"],
1369
      ["node25", "node1"],
1370
      ["node1", "node44"],
1371
      ])
1372

    
1373
    # Name field, but no sorting, result must be in incoming order
1374
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1375
    self.assertFalse(q.RequestedData())
1376
    self.assertEqual(q.Query(data, sort_by_name=False),
1377
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1378
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1379
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1380
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1381
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1382
      ["node1", "node44"],
1383
      ["node30", "node90"],
1384
      ["node25", "node1"],
1385
      ["node20", "node1"],
1386
      ])
1387
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1388
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1389
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1390
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1391
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1392
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1393
      ["node20", "node1"],
1394
      ["node25", "node1"],
1395
      ["node30", "node90"],
1396
      ["node1", "node44"],
1397
      ])
1398

    
1399
  def testEqualNamesOrder(self):
1400
    fielddefs = query._PrepareFieldList([
1401
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1402
       None, 0, lambda ctx, item: item["pnode"]),
1403
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1404
       None, 0, lambda ctx, item: item["num"]),
1405
      ], [])
1406

    
1407
    data = [
1408
      { "pnode": "node1", "num": 100, },
1409
      { "pnode": "node1", "num": 25, },
1410
      { "pnode": "node2", "num": 90, },
1411
      { "pnode": "node2", "num": 30, },
1412
      ]
1413

    
1414
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1415
                    qfilter=["|", ["=", "pnode", "node1"],
1416
                                  ["=", "pnode", "node2"],
1417
                                  ["=", "pnode", "node1"]])
1418
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1419
                     msg="Did not return unique names")
1420
    self.assertFalse(q.RequestedData())
1421
    self.assertEqual(q.Query(data),
1422
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1423
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1424
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1425
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1426
    self.assertEqual(q.Query(data, sort_by_name=False),
1427
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1428
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1429
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1430
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1431

    
1432
    data = [
1433
      { "pnode": "nodeX", "num": 50, },
1434
      { "pnode": "nodeY", "num": 40, },
1435
      { "pnode": "nodeX", "num": 30, },
1436
      { "pnode": "nodeX", "num": 20, },
1437
      { "pnode": "nodeM", "num": 10, },
1438
      ]
1439

    
1440
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1441
                    qfilter=["|", ["=", "pnode", "nodeX"],
1442
                                  ["=", "pnode", "nodeY"],
1443
                                  ["=", "pnode", "nodeY"],
1444
                                  ["=", "pnode", "nodeY"],
1445
                                  ["=", "pnode", "nodeM"]])
1446
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1447
                     msg="Did not return unique names")
1448
    self.assertFalse(q.RequestedData())
1449

    
1450
    # First sorted by name, then input order
1451
    self.assertEqual(q.Query(data, sort_by_name=True),
1452
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1453
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1454
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1455
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1456
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1457

    
1458
    # Input order
1459
    self.assertEqual(q.Query(data, sort_by_name=False),
1460
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1461
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1462
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1463
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1464
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1465

    
1466
  def testFilter(self):
1467
    (DK_A, DK_B) = range(1000, 1002)
1468

    
1469
    fielddefs = query._PrepareFieldList([
1470
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1471
       DK_A, 0, lambda ctx, item: item["name"]),
1472
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1473
       DK_B, 0, lambda ctx, item: item["other"]),
1474
      ], [])
1475

    
1476
    data = [
1477
      { "name": "node1", "other": "foo", },
1478
      { "name": "node2", "other": "bar", },
1479
      { "name": "node3", "other": "Hello", },
1480
      ]
1481

    
1482
    # Empty filter
1483
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1484
                    qfilter=["|"])
1485
    self.assertTrue(q.RequestedNames() is None)
1486
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1487
    self.assertEqual(q.Query(data), [])
1488

    
1489
    # Normal filter
1490
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1491
                    qfilter=["=", "name", "node1"])
1492
    self.assertEqual(q.RequestedNames(), ["node1"])
1493
    self.assertEqual(q.Query(data),
1494
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1495

    
1496
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1497
                    qfilter=(["|", ["=", "name", "node1"],
1498
                                   ["=", "name", "node3"]]))
1499
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1500
    self.assertEqual(q.Query(data),
1501
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1502
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1503

    
1504
    # Complex filter
1505
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1506
                    qfilter=(["|", ["=", "name", "node1"],
1507
                                   ["|", ["=", "name", "node3"],
1508
                                         ["=", "name", "node2"]],
1509
                                   ["=", "name", "node3"]]))
1510
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1511
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1512
    self.assertEqual(q.Query(data),
1513
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1514
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1515
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1516

    
1517
    # Filter data type mismatch
1518
    for i in [-1, 0, 1, 123, [], None, True, False]:
1519
      self.assertRaises(errors.ParameterError, query.Query,
1520
                        fielddefs, ["name", "other"], namefield="name",
1521
                        qfilter=["=", "name", i])
1522

    
1523
    # Negative filter
1524
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1525
                    qfilter=["!", ["|", ["=", "name", "node1"],
1526
                                        ["=", "name", "node3"]]])
1527
    self.assertTrue(q.RequestedNames() is None)
1528
    self.assertEqual(q.Query(data),
1529
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1530

    
1531
    # Not equal
1532
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1533
                    qfilter=["!=", "name", "node3"])
1534
    self.assertTrue(q.RequestedNames() is None)
1535
    self.assertEqual(q.Query(data),
1536
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1537
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1538

    
1539
    # Data type
1540
    q = query.Query(fielddefs, [], namefield="name",
1541
                    qfilter=["|", ["=", "other", "bar"],
1542
                                  ["=", "name", "foo"]])
1543
    self.assertTrue(q.RequestedNames() is None)
1544
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1545
    self.assertEqual(q.Query(data), [[]])
1546

    
1547
    # Only one data type
1548
    q = query.Query(fielddefs, ["other"], namefield="name",
1549
                    qfilter=["=", "other", "bar"])
1550
    self.assertTrue(q.RequestedNames() is None)
1551
    self.assertEqual(q.RequestedData(), set([DK_B]))
1552
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1553

    
1554
    q = query.Query(fielddefs, [], namefield="name",
1555
                    qfilter=["=", "other", "bar"])
1556
    self.assertTrue(q.RequestedNames() is None)
1557
    self.assertEqual(q.RequestedData(), set([DK_B]))
1558
    self.assertEqual(q.Query(data), [[]])
1559

    
1560
    # Data type in boolean operator
1561
    q = query.Query(fielddefs, [], namefield="name",
1562
                    qfilter=["?", "name"])
1563
    self.assertTrue(q.RequestedNames() is None)
1564
    self.assertEqual(q.RequestedData(), set([DK_A]))
1565
    self.assertEqual(q.Query(data), [[], [], []])
1566

    
1567
    q = query.Query(fielddefs, [], namefield="name",
1568
                    qfilter=["!", ["?", "name"]])
1569
    self.assertTrue(q.RequestedNames() is None)
1570
    self.assertEqual(q.RequestedData(), set([DK_A]))
1571
    self.assertEqual(q.Query(data), [])
1572

    
1573
  def testFilterContains(self):
1574
    fielddefs = query._PrepareFieldList([
1575
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1576
       None, 0, lambda ctx, item: item["name"]),
1577
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1578
       None, 0, lambda ctx, item: item["other"]),
1579
      ], [])
1580

    
1581
    data = [
1582
      { "name": "node2", "other": ["x", "y", "bar"], },
1583
      { "name": "node3", "other": "Hello", },
1584
      { "name": "node1", "other": ["a", "b", "foo"], },
1585
      { "name": "empty", "other": []},
1586
      ]
1587

    
1588
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1589
                    qfilter=["=[]", "other", "bar"])
1590
    self.assertTrue(q.RequestedNames() is None)
1591
    self.assertEqual(q.Query(data), [
1592
      [(constants.RS_NORMAL, "node2"),
1593
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1594
      ])
1595

    
1596
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1597
                    qfilter=["|", ["=[]", "other", "bar"],
1598
                                  ["=[]", "other", "a"],
1599
                                  ["=[]", "other", "b"]])
1600
    self.assertTrue(q.RequestedNames() is None)
1601
    self.assertEqual(q.Query(data), [
1602
      [(constants.RS_NORMAL, "node1"),
1603
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1604
      [(constants.RS_NORMAL, "node2"),
1605
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1606
      ])
1607
    self.assertEqual(q.OldStyleQuery(data), [
1608
      ["node1", ["a", "b", "foo"]],
1609
      ["node2", ["x", "y", "bar"]],
1610
      ])
1611

    
1612
    # Boolean test
1613
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1614
                    qfilter=["?", "other"])
1615
    self.assertEqual(q.OldStyleQuery(data), [
1616
      ["node1", ["a", "b", "foo"]],
1617
      ["node2", ["x", "y", "bar"]],
1618
      ["node3", "Hello"],
1619
      ])
1620

    
1621
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1622
                    qfilter=["!", ["?", "other"]])
1623
    self.assertEqual(q.OldStyleQuery(data), [
1624
      ["empty", []],
1625
      ])
1626

    
1627
  def testFilterHostname(self):
1628
    fielddefs = query._PrepareFieldList([
1629
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1630
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1631
      ], [])
1632

    
1633
    data = [
1634
      { "name": "node1.example.com", },
1635
      { "name": "node2.example.com", },
1636
      { "name": "node2.example.net", },
1637
      ]
1638

    
1639
    q = query.Query(fielddefs, ["name"], namefield="name",
1640
                    qfilter=["=", "name", "node2"])
1641
    self.assertEqual(q.RequestedNames(), ["node2"])
1642
    self.assertEqual(q.Query(data), [
1643
      [(constants.RS_NORMAL, "node2.example.com")],
1644
      [(constants.RS_NORMAL, "node2.example.net")],
1645
      ])
1646

    
1647
    q = query.Query(fielddefs, ["name"], namefield="name",
1648
                    qfilter=["=", "name", "node1"])
1649
    self.assertEqual(q.RequestedNames(), ["node1"])
1650
    self.assertEqual(q.Query(data), [
1651
      [(constants.RS_NORMAL, "node1.example.com")],
1652
      ])
1653

    
1654
    q = query.Query(fielddefs, ["name"], namefield="name",
1655
                    qfilter=["=", "name", "othername"])
1656
    self.assertEqual(q.RequestedNames(), ["othername"])
1657
    self.assertEqual(q.Query(data), [])
1658

    
1659
    q = query.Query(fielddefs, ["name"], namefield="name",
1660
                    qfilter=["|", ["=", "name", "node1.example.com"],
1661
                                  ["=", "name", "node2"]])
1662
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1663
    self.assertEqual(q.Query(data), [
1664
      [(constants.RS_NORMAL, "node1.example.com")],
1665
      [(constants.RS_NORMAL, "node2.example.com")],
1666
      [(constants.RS_NORMAL, "node2.example.net")],
1667
      ])
1668
    self.assertEqual(q.OldStyleQuery(data), [
1669
      ["node1.example.com"],
1670
      ["node2.example.com"],
1671
      ["node2.example.net"],
1672
      ])
1673

    
1674
    q = query.Query(fielddefs, ["name"], namefield="name",
1675
                    qfilter=["!=", "name", "node1"])
1676
    self.assertTrue(q.RequestedNames() is None)
1677
    self.assertEqual(q.Query(data), [
1678
      [(constants.RS_NORMAL, "node2.example.com")],
1679
      [(constants.RS_NORMAL, "node2.example.net")],
1680
      ])
1681
    self.assertEqual(q.OldStyleQuery(data), [
1682
      ["node2.example.com"],
1683
      ["node2.example.net"],
1684
      ])
1685

    
1686
  def testFilterBoolean(self):
1687
    fielddefs = query._PrepareFieldList([
1688
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1689
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1690
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1691
       None, 0, lambda ctx, item: item["value"]),
1692
      ], [])
1693

    
1694
    data = [
1695
      { "name": "node1", "value": False, },
1696
      { "name": "node2", "value": True, },
1697
      { "name": "node3", "value": True, },
1698
      ]
1699

    
1700
    q = query.Query(fielddefs, ["name", "value"],
1701
                    qfilter=["|", ["=", "value", False],
1702
                                  ["=", "value", True]])
1703
    self.assertTrue(q.RequestedNames() is None)
1704
    self.assertEqual(q.Query(data), [
1705
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1706
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1707
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1708
      ])
1709

    
1710
    q = query.Query(fielddefs, ["name", "value"],
1711
                    qfilter=["|", ["=", "value", False],
1712
                                  ["!", ["=", "value", False]]])
1713
    self.assertTrue(q.RequestedNames() is None)
1714
    self.assertEqual(q.Query(data), [
1715
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1716
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1717
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1718
      ])
1719

    
1720
    # Comparing bool with string
1721
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1722
      self.assertRaises(errors.ParameterError, query.Query,
1723
                        fielddefs, ["name", "value"],
1724
                        qfilter=["=", "value", i])
1725

    
1726
    # Truth filter
1727
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1728
    self.assertTrue(q.RequestedNames() is None)
1729
    self.assertEqual(q.Query(data), [
1730
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1731
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1732
      ])
1733

    
1734
    # Negative bool filter
1735
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1736
    self.assertTrue(q.RequestedNames() is None)
1737
    self.assertEqual(q.Query(data), [
1738
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1739
      ])
1740

    
1741
    # Complex truth filter
1742
    q = query.Query(fielddefs, ["name", "value"],
1743
                    qfilter=["|", ["&", ["=", "name", "node1"],
1744
                                        ["!", ["?", "value"]]],
1745
                                  ["?", "value"]])
1746
    self.assertTrue(q.RequestedNames() is None)
1747
    self.assertEqual(q.Query(data), [
1748
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1749
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1750
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1751
      ])
1752

    
1753
  def testFilterRegex(self):
1754
    fielddefs = query._PrepareFieldList([
1755
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1756
       None, 0, lambda ctx, item: item["name"]),
1757
      ], [])
1758

    
1759
    data = [
1760
      { "name": "node1.example.com", },
1761
      { "name": "node2.site.example.com", },
1762
      { "name": "node2.example.net", },
1763

    
1764
      # Empty name
1765
      { "name": "", },
1766
      ]
1767

    
1768
    q = query.Query(fielddefs, ["name"], namefield="name",
1769
                    qfilter=["=~", "name", "site"])
1770
    self.assertTrue(q.RequestedNames() is None)
1771
    self.assertEqual(q.Query(data), [
1772
      [(constants.RS_NORMAL, "node2.site.example.com")],
1773
      ])
1774

    
1775
    q = query.Query(fielddefs, ["name"], namefield="name",
1776
                    qfilter=["=~", "name", "^node2"])
1777
    self.assertTrue(q.RequestedNames() is None)
1778
    self.assertEqual(q.Query(data), [
1779
      [(constants.RS_NORMAL, "node2.example.net")],
1780
      [(constants.RS_NORMAL, "node2.site.example.com")],
1781
      ])
1782

    
1783
    q = query.Query(fielddefs, ["name"], namefield="name",
1784
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1785
    self.assertTrue(q.RequestedNames() is None)
1786
    self.assertEqual(q.Query(data), [
1787
      [(constants.RS_NORMAL, "node1.example.com")],
1788
      [(constants.RS_NORMAL, "node2.site.example.com")],
1789
      ])
1790

    
1791
    q = query.Query(fielddefs, ["name"], namefield="name",
1792
                    qfilter=["=~", "name", r"."])
1793
    self.assertTrue(q.RequestedNames() is None)
1794
    self.assertEqual(q.Query(data), [
1795
      [(constants.RS_NORMAL, "node1.example.com")],
1796
      [(constants.RS_NORMAL, "node2.example.net")],
1797
      [(constants.RS_NORMAL, "node2.site.example.com")],
1798
      ])
1799

    
1800
    q = query.Query(fielddefs, ["name"], namefield="name",
1801
                    qfilter=["=~", "name", r"^$"])
1802
    self.assertTrue(q.RequestedNames() is None)
1803
    self.assertEqual(q.Query(data), [
1804
      [(constants.RS_NORMAL, "")],
1805
      ])
1806

    
1807
    # Invalid regular expression
1808
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1809
                      qfilter=["=~", "name", r"["])
1810

    
1811
  def testFilterLessGreater(self):
1812
    fielddefs = query._PrepareFieldList([
1813
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1814
       None, 0, lambda ctx, item: item),
1815
      ], [])
1816

    
1817
    data = range(100)
1818

    
1819
    q = query.Query(fielddefs, ["value"],
1820
                    qfilter=["<", "value", 20])
1821
    self.assertTrue(q.RequestedNames() is None)
1822
    self.assertEqual(q.Query(data),
1823
                     [[(constants.RS_NORMAL, i)] for i in range(20)])
1824

    
1825
    q = query.Query(fielddefs, ["value"],
1826
                    qfilter=["<=", "value", 30])
1827
    self.assertTrue(q.RequestedNames() is None)
1828
    self.assertEqual(q.Query(data),
1829
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1830

    
1831
    q = query.Query(fielddefs, ["value"],
1832
                    qfilter=[">", "value", 40])
1833
    self.assertTrue(q.RequestedNames() is None)
1834
    self.assertEqual(q.Query(data),
1835
                     [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
1836

    
1837
    q = query.Query(fielddefs, ["value"],
1838
                    qfilter=[">=", "value", 50])
1839
    self.assertTrue(q.RequestedNames() is None)
1840
    self.assertEqual(q.Query(data),
1841
                     [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
1842

    
1843
  def testFilterLessGreaterJobId(self):
1844
    fielddefs = query._PrepareFieldList([
1845
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1846
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1847
      ], [])
1848

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

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

    
1853
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1854
    self.assertTrue(q.RequestedNames() is None)
1855
    self.assertEqual(q.Query(data), [
1856
      [(constants.RS_NORMAL, "1")],
1857
      [(constants.RS_NORMAL, "2")],
1858
      [(constants.RS_NORMAL, "3")],
1859
      [(constants.RS_NORMAL, "10")],
1860
      [(constants.RS_NORMAL, "15")],
1861
      [(constants.RS_NORMAL, "7")],
1862
      ])
1863

    
1864
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1865
    self.assertTrue(q.RequestedNames() is None)
1866
    self.assertEqual(q.Query(data), [
1867
      [(constants.RS_NORMAL, "102")],
1868
      [(constants.RS_NORMAL, "120")],
1869
      [(constants.RS_NORMAL, "125")],
1870
      [(constants.RS_NORMAL, "100")],
1871
      ])
1872

    
1873
    # Integers are no valid job IDs
1874
    self.assertRaises(errors.ParameterError, query.Query,
1875
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1876

    
1877
  def testFilterLessGreaterSplitTimestamp(self):
1878
    fielddefs = query._PrepareFieldList([
1879
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1880
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1881
      ], [])
1882

    
1883
    data = [
1884
      utils.SplitTime(0),
1885
      utils.SplitTime(0.1),
1886
      utils.SplitTime(18224.7872),
1887
      utils.SplitTime(919896.12623),
1888
      utils.SplitTime(999),
1889
      utils.SplitTime(989.9999),
1890
      ]
1891

    
1892
    for i in [0, [0, 0]]:
1893
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1894
      self.assertTrue(q.RequestedNames() is None)
1895
      self.assertEqual(q.Query(data), [])
1896

    
1897
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1898
    self.assertTrue(q.RequestedNames() is None)
1899
    self.assertEqual(q.Query(data), [
1900
      [(constants.RS_NORMAL, (0, 0))],
1901
      [(constants.RS_NORMAL, (0, 100000))],
1902
      [(constants.RS_NORMAL, (999, 0))],
1903
      [(constants.RS_NORMAL, (989, 999900))],
1904
      ])
1905

    
1906
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
1907
    self.assertTrue(q.RequestedNames() is None)
1908
    self.assertEqual(q.Query(data), [
1909
      [(constants.RS_NORMAL, (18224, 787200))],
1910
      [(constants.RS_NORMAL, (919896, 126230))],
1911
      ])
1912

    
1913
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1914
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1915
      self.assertTrue(q.RequestedNames() is None)
1916
      self.assertEqual(q.Query(data), [
1917
        [(constants.RS_NORMAL, (18224, 787200))],
1918
        [(constants.RS_NORMAL, (919896, 126230))],
1919
        ])
1920

    
1921
    q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880])
1922
    self.assertTrue(q.RequestedNames() is None)
1923
    self.assertEqual(q.Query(data), [
1924
      [(constants.RS_NORMAL, (919896, 126230))],
1925
      ])
1926

    
1927

    
1928
if __name__ == "__main__":
1929
  testutils.GanetiTestProgram()