Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.query_unittest.py @ da4a52a3

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

    
303
    checks = [
304
      (constants.NR_MASTER, "node1", objects.Node(name="node1")),
305
      (constants.NR_MCANDIDATE, "master",
306
       objects.Node(name="node1", master_candidate=True)),
307
      (constants.NR_REGULAR, "master", objects.Node(name="node1")),
308
      (constants.NR_DRAINED, "master",
309
       objects.Node(name="node1", drained=True)),
310
      (constants.NR_OFFLINE,
311
       "master", objects.Node(name="node1", offline=True)),
312
      ]
313

    
314
    for (role, master_name, node) in checks:
315
      result = query._GetNodeRole(node, master_name)
316
      self.assertEqual(result, role)
317
      tested_role.add(result)
318

    
319
    self.assertEqual(tested_role, constants.NR_ALL)
320

    
321

    
322
class TestNodeQuery(unittest.TestCase):
323
  def _Create(self, selected):
324
    return query.Query(query.NODE_FIELDS, selected)
325

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

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

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

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

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

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

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

    
426
    live_data_node = nodes[4]
427
    assert live_data_node.name != master_name
428

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

    
443
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
444
            sorted(fake_live_data.keys()))
445

    
446
    live_data = dict.fromkeys([node.uuid for node in nodes], {})
447
    live_data[live_data_node.uuid] = \
448
      dict((query._NODE_LIVE_FIELDS[name][2], value)
449
           for name, value in fake_live_data.items())
450

    
451
    node_to_primary_uuid = dict((node.uuid, set()) for node in nodes)
452
    node_to_primary_uuid[master_node.uuid].update(["inst1", "inst2"])
453

    
454
    node_to_secondary_uuid = dict((node.uuid, set()) for node in nodes)
455
    node_to_secondary_uuid[live_data_node.uuid].update(["instX", "instY",
456
                                                        "instZ"])
457

    
458
    inst_uuid_to_inst_name = {
459
      "inst1": "inst1-name",
460
      "inst2": "inst2-name",
461
      "instX": "instX-name",
462
      "instY": "instY-name",
463
      "instZ": "instZ-name"
464
    }
465

    
466
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
467
    groups = {
468
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
469
      }
470

    
471
    oob_not_powered_node = nodes[0]
472
    oob_not_powered_node.powered = False
473
    oob_support = dict((node.uuid, False) for node in nodes)
474
    oob_support[master_node.uuid] = True
475
    oob_support[oob_not_powered_node.uuid] = True
476

    
477
    master_node.group = ng_uuid
478

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

    
488
    node_to_row = dict((row[field_index["name"]][1], idx)
489
                       for idx, row in enumerate(result))
490

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

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

    
524
    live_data_row = result[node_to_row[live_data_node.name]]
525

    
526
    for (field, value) in fake_live_data.items():
527
      self.assertEqual(live_data_row[field_index[field]],
528
                       (constants.RS_NORMAL, value))
529

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

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

    
559
    # No data
560
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None,
561
                              None)
562
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
563
                                             nqd, nodes[0]),
564
                     query._FS_NODATA)
565

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

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

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

    
591
    # Wrong field type
592
    ctx = _QueryData(None, curlive_data={"hello": 123})
593
    self.assertRaises(AssertionError, query._GetLiveNodeField,
594
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
595

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

    
603

    
604
class TestInstanceQuery(unittest.TestCase):
605
  def _Create(self, selected):
606
    return query.Query(query.INSTANCE_FIELDS, selected)
607

    
608
  def testSimple(self):
609
    q = self._Create(["name", "be/maxmem", "ip"])
610
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
611

    
612
    cluster = objects.Cluster(cluster_name="testcluster",
613
      hvparams=constants.HVC_DEFAULTS,
614
      beparams={
615
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
616
        },
617
      nicparams={
618
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
619
        },
620
      os_hvp={},
621
      osparams={})
622

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

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

    
655
  def test(self):
656
    selected = query.INSTANCE_FIELDS.keys()
657
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
658

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

    
662
    q = self._Create(selected)
663
    self.assertEqual(q.RequestedData(),
664
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
665
                          query.IQ_CONSOLE, query.IQ_NODES, query.IQ_NETWORKS]))
666

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

    
683
    offline_nodes = ["nodeoff1-uuid", "nodeoff2-uuid"]
684
    bad_nodes = ["nodebad1-uuid", "nodebad2-uuid", "nodebad3-uuid"] +\
685
                offline_nodes
686
    node_uuids = ["node%s-uuid" % i for i in range(10)] + bad_nodes
687

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

    
810
    assert not utils.FindDuplicates(inst.uuid for inst in instances)
811
    assert not utils.FindDuplicates(inst.name for inst in instances)
812

    
813
    instbyname = dict((inst.name, inst) for inst in instances)
814

    
815
    disk_usage = dict((inst.uuid,
816
                       gmi.ComputeDiskSize(inst.disk_template,
817
                                           [{"size": disk.size}
818
                                           for disk in inst.disks]))
819
                      for inst in instances)
820

    
821
    inst_bridges = {
822
      "inst3-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
823
      "inst4-uuid": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
824
                     None, "eth123"],
825
      }
826

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

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

    
850
    nodes = dict([(uuid, objects.Node(
851
                           name="%s.example.com" % uuid,
852
                           uuid=uuid,
853
                           group="default-uuid"))
854
                  for uuid in node_uuids])
855

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

    
864
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
865
           "Offline nodes not included in bad nodes"
866

    
867
    tested_status = set()
868

    
869
    for (inst, row) in zip(instances, result):
870
      assert inst.primary_node in node_uuids
871

    
872
      self.assertEqual(row[fieldidx["name"]],
873
                       (constants.RS_NORMAL, inst.name))
874

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

    
893
      self.assertEqual(row[fieldidx["status"]],
894
                       (constants.RS_NORMAL, exp_status))
895

    
896
      (_, status) = row[fieldidx["status"]]
897
      tested_status.add(status)
898

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

    
912
        self.assertEqual(row[fieldidx[field]], exp)
913

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

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

    
931
      if inst.primary_node in bad_nodes:
932
        exp = (constants.RS_NODATA, None)
933
      else:
934
        exp = (constants.RS_NORMAL, inst.uuid in live_data)
935
      self.assertEqual(row[fieldidx["oper_state"]], exp)
936

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

    
949
      usage = disk_usage[inst.uuid]
950
      if usage is None:
951
        usage = 0
952
      self.assertEqual(row[fieldidx["disk_usage"]],
953
                       (constants.RS_NORMAL, usage))
954

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

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

    
974
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
975

    
976
    # Ensure all possible status' have been tested
977
    self.assertEqual(tested_status, constants.INSTST_ALL)
978

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

    
988

    
989
class TestGroupQuery(unittest.TestCase):
990

    
991
  def setUp(self):
992
    self.custom_diskparams = {
993
      constants.DT_DRBD8: {
994
        constants.DRBD_DEFAULT_METAVG: "foobar",
995
      },
996
    }
997

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

    
1027
  def _Create(self, selected):
1028
    return query.Query(query.GROUP_FIELDS, selected)
1029

    
1030
  def testSimple(self):
1031
    q = self._Create(["name", "uuid", "alloc_policy"])
1032
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1033

    
1034
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1035

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

    
1047
  def testNodes(self):
1048
    groups_to_nodes = {
1049
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1050
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1051
      }
1052

    
1053
    q = self._Create(["name", "node_cnt", "node_list"])
1054
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1055
                               False)
1056

    
1057
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1058

    
1059
    self.assertEqual(q.Query(gqd),
1060
                     [[(constants.RS_NORMAL, "default"),
1061
                       (constants.RS_NORMAL, 2),
1062
                       (constants.RS_NORMAL, ["node1", "node2"]),
1063
                       ],
1064
                      [(constants.RS_NORMAL, "restricted"),
1065
                       (constants.RS_NORMAL, 3),
1066
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1067
                       ],
1068
                      ])
1069

    
1070
  def testInstances(self):
1071
    groups_to_instances = {
1072
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1073
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1074
      }
1075

    
1076
    q = self._Create(["pinst_cnt", "pinst_list"])
1077
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1078
      groups_to_instances, False)
1079

    
1080
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1081

    
1082
    self.assertEqual(q.Query(gqd),
1083
                     [[(constants.RS_NORMAL, 2),
1084
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1085
                       ],
1086
                      [(constants.RS_NORMAL, 3),
1087
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1088
                       ],
1089
                      ])
1090

    
1091
  def testDiskparams(self):
1092
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1093
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1094

    
1095
    self.assertEqual(q.RequestedData(),
1096
                     set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1097

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

    
1112

    
1113
class TestOsQuery(unittest.TestCase):
1114
  def _Create(self, selected):
1115
    return query.Query(query.OS_FIELDS, selected)
1116

    
1117
  def test(self):
1118
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1119
    api_versions = [10, 0, 15, 5]
1120
    parameters = ["zpar3", "apar9"]
1121

    
1122
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1123
    assert (api_versions != sorted(api_versions) and
1124
            api_versions != utils.NiceSort(variants))
1125
    assert (parameters != sorted(parameters) and
1126
            parameters != utils.NiceSort(parameters))
1127

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

    
1139

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

    
1164

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

    
1176
  def testSomeFields(self):
1177
    rnd = random.Random(5357)
1178

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

    
1193

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

    
1213
      assert namefield in fielddefs
1214

    
1215
      reqnames = [genval(i) for i in range(4)]
1216
      innerfilter = [["=", namefield, v] for v in reqnames]
1217

    
1218
      # No name field
1219
      q = query.Query(fielddefs, [namefield],
1220
                      qfilter=["=", namefield, nameval], namefield=None)
1221
      self.assertEqual(q.RequestedNames(), None)
1222

    
1223
      # No filter
1224
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1225
      self.assertEqual(q.RequestedNames(), None)
1226

    
1227
      # Check empty query
1228
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1229
                      namefield=namefield)
1230
      self.assertEqual(q.RequestedNames(), None)
1231

    
1232
      # Check order
1233
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1234
                      namefield=namefield)
1235
      self.assertEqual(q.RequestedNames(), reqnames)
1236

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

    
1243
      # Duplicates
1244
      q = query.Query(fielddefs, [namefield],
1245
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1246
                      namefield=namefield)
1247
      self.assertEqual(q.RequestedNames(), reqnames)
1248

    
1249
      # Unknown name field
1250
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1251
                        namefield="_unknown_field_")
1252

    
1253
      # Filter with AND
1254
      q = query.Query(fielddefs, [namefield],
1255
                      qfilter=["|", ["=", namefield, nameval],
1256
                                    ["&", ["=", namefield, namevalempty]]],
1257
                      namefield=namefield)
1258
      self.assertTrue(q.RequestedNames() is None)
1259

    
1260
      # Filter with NOT
1261
      q = query.Query(fielddefs, [namefield],
1262
                      qfilter=["|", ["=", namefield, nameval],
1263
                                    ["!", ["=", namefield, namevalempty]]],
1264
                      namefield=namefield)
1265
      self.assertTrue(q.RequestedNames() is None)
1266

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

    
1276
  @staticmethod
1277
  def _GenNestedFilter(namefield, op, depth, nameval):
1278
    nested = ["=", namefield, nameval]
1279
    for i in range(depth):
1280
      nested = [op, nested]
1281
    return nested
1282

    
1283
  def testCompileFilter(self):
1284
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1285

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

    
1297
      checks = [
1298
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1299
        ["=", "_unknown_field", "value"],
1300
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1301
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1302
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1303
        ]
1304

    
1305
      for qfilter in checks:
1306
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1307
                          fielddefs, None, qfilter)
1308

    
1309
      for op in ["|", "!"]:
1310
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1311
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1312
                                                      qfilter)))
1313

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

    
1322
    data = [
1323
      { "pnode": "node1", "snode": "node44", },
1324
      { "pnode": "node30", "snode": "node90", },
1325
      { "pnode": "node25", "snode": "node1", },
1326
      { "pnode": "node20", "snode": "node1", },
1327
      ]
1328

    
1329
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1330

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

    
1340
    # Try again with reversed input data
1341
    self.assertEqual(q.Query(reversed(data)),
1342
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1343
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1344
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1345

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

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

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

    
1403
    data = [
1404
      { "pnode": "node1", "num": 100, },
1405
      { "pnode": "node1", "num": 25, },
1406
      { "pnode": "node2", "num": 90, },
1407
      { "pnode": "node2", "num": 30, },
1408
      ]
1409

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

    
1428
    data = [
1429
      { "pnode": "nodeX", "num": 50, },
1430
      { "pnode": "nodeY", "num": 40, },
1431
      { "pnode": "nodeX", "num": 30, },
1432
      { "pnode": "nodeX", "num": 20, },
1433
      { "pnode": "nodeM", "num": 10, },
1434
      ]
1435

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

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

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

    
1462
  def testFilter(self):
1463
    (DK_A, DK_B) = range(1000, 1002)
1464

    
1465
    fielddefs = query._PrepareFieldList([
1466
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1467
       DK_A, 0, lambda ctx, item: item["name"]),
1468
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1469
       DK_B, 0, lambda ctx, item: item["other"]),
1470
      ], [])
1471

    
1472
    data = [
1473
      { "name": "node1", "other": "foo", },
1474
      { "name": "node2", "other": "bar", },
1475
      { "name": "node3", "other": "Hello", },
1476
      ]
1477

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

    
1485
    # Normal filter
1486
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1487
                    qfilter=["=", "name", "node1"])
1488
    self.assertEqual(q.RequestedNames(), ["node1"])
1489
    self.assertEqual(q.Query(data),
1490
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1491

    
1492
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1493
                    qfilter=(["|", ["=", "name", "node1"],
1494
                                   ["=", "name", "node3"]]))
1495
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1496
    self.assertEqual(q.Query(data),
1497
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1498
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1499

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

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

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

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

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

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

    
1550
    q = query.Query(fielddefs, [], namefield="name",
1551
                    qfilter=["=", "other", "bar"])
1552
    self.assertTrue(q.RequestedNames() is None)
1553
    self.assertEqual(q.RequestedData(), set([DK_B]))
1554
    self.assertEqual(q.Query(data), [[]])
1555

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

    
1563
    q = query.Query(fielddefs, [], namefield="name",
1564
                    qfilter=["!", ["?", "name"]])
1565
    self.assertTrue(q.RequestedNames() is None)
1566
    self.assertEqual(q.RequestedData(), set([DK_A]))
1567
    self.assertEqual(q.Query(data), [])
1568

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

    
1577
    data = [
1578
      { "name": "node2", "other": ["x", "y", "bar"], },
1579
      { "name": "node3", "other": "Hello", },
1580
      { "name": "node1", "other": ["a", "b", "foo"], },
1581
      { "name": "empty", "other": []},
1582
      ]
1583

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

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

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

    
1617
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1618
                    qfilter=["!", ["?", "other"]])
1619
    self.assertEqual(q.OldStyleQuery(data), [
1620
      ["empty", []],
1621
      ])
1622

    
1623
  def testFilterHostname(self):
1624
    fielddefs = query._PrepareFieldList([
1625
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1626
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1627
      ], [])
1628

    
1629
    data = [
1630
      { "name": "node1.example.com", },
1631
      { "name": "node2.example.com", },
1632
      { "name": "node2.example.net", },
1633
      ]
1634

    
1635
    q = query.Query(fielddefs, ["name"], namefield="name",
1636
                    qfilter=["=", "name", "node2"])
1637
    self.assertEqual(q.RequestedNames(), ["node2"])
1638
    self.assertEqual(q.Query(data), [
1639
      [(constants.RS_NORMAL, "node2.example.com")],
1640
      [(constants.RS_NORMAL, "node2.example.net")],
1641
      ])
1642

    
1643
    q = query.Query(fielddefs, ["name"], namefield="name",
1644
                    qfilter=["=", "name", "node1"])
1645
    self.assertEqual(q.RequestedNames(), ["node1"])
1646
    self.assertEqual(q.Query(data), [
1647
      [(constants.RS_NORMAL, "node1.example.com")],
1648
      ])
1649

    
1650
    q = query.Query(fielddefs, ["name"], namefield="name",
1651
                    qfilter=["=", "name", "othername"])
1652
    self.assertEqual(q.RequestedNames(), ["othername"])
1653
    self.assertEqual(q.Query(data), [])
1654

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

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

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

    
1690
    data = [
1691
      { "name": "node1", "value": False, },
1692
      { "name": "node2", "value": True, },
1693
      { "name": "node3", "value": True, },
1694
      ]
1695

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

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

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

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

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

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

    
1749
  def testFilterRegex(self):
1750
    fielddefs = query._PrepareFieldList([
1751
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1752
       None, 0, lambda ctx, item: item["name"]),
1753
      ], [])
1754

    
1755
    data = [
1756
      { "name": "node1.example.com", },
1757
      { "name": "node2.site.example.com", },
1758
      { "name": "node2.example.net", },
1759

    
1760
      # Empty name
1761
      { "name": "", },
1762
      ]
1763

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

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

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

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

    
1796
    q = query.Query(fielddefs, ["name"], namefield="name",
1797
                    qfilter=["=~", "name", r"^$"])
1798
    self.assertTrue(q.RequestedNames() is None)
1799
    self.assertEqual(q.Query(data), [
1800
      [(constants.RS_NORMAL, "")],
1801
      ])
1802

    
1803
    # Invalid regular expression
1804
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1805
                      qfilter=["=~", "name", r"["])
1806

    
1807
  def testFilterLessGreater(self):
1808
    fielddefs = query._PrepareFieldList([
1809
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1810
       None, 0, lambda ctx, item: item),
1811
      ], [])
1812

    
1813
    data = range(100)
1814

    
1815
    q = query.Query(fielddefs, ["value"],
1816
                    qfilter=["<", "value", 20])
1817
    self.assertTrue(q.RequestedNames() is None)
1818
    self.assertEqual(q.Query(data),
1819
                     [[(constants.RS_NORMAL, i)] for i in range(20)])
1820

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

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

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

    
1839
  def testFilterLessGreaterJobId(self):
1840
    fielddefs = query._PrepareFieldList([
1841
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1842
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1843
      ], [])
1844

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

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

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

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

    
1869
    # Integers are no valid job IDs
1870
    self.assertRaises(errors.ParameterError, query.Query,
1871
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1872

    
1873
  def testFilterLessGreaterSplitTimestamp(self):
1874
    fielddefs = query._PrepareFieldList([
1875
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1876
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1877
      ], [])
1878

    
1879
    data = [
1880
      utils.SplitTime(0),
1881
      utils.SplitTime(0.1),
1882
      utils.SplitTime(18224.7872),
1883
      utils.SplitTime(919896.12623),
1884
      utils.SplitTime(999),
1885
      utils.SplitTime(989.9999),
1886
      ]
1887

    
1888
    for i in [0, [0, 0]]:
1889
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1890
      self.assertTrue(q.RequestedNames() is None)
1891
      self.assertEqual(q.Query(data), [])
1892

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

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

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

    
1917
    q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880])
1918
    self.assertTrue(q.RequestedNames() is None)
1919
    self.assertEqual(q.Query(data), [
1920
      [(constants.RS_NORMAL, (919896, 126230))],
1921
      ])
1922

    
1923

    
1924
if __name__ == "__main__":
1925
  testutils.GanetiTestProgram()