Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (73.3 kB)

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

    
4
# Copyright (C) 2010, 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for testing ganeti.query"""
23

    
24
import re
25
import unittest
26
import random
27

    
28
from ganeti import constants
29
from ganeti import utils
30
from ganeti import compat
31
from ganeti import errors
32
from ganeti import query
33
from ganeti import objects
34
from ganeti import cmdlib
35

    
36
import 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,
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_name = node_names[4]
427
    assert live_data_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
      }
440

    
441
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
442
            sorted(fake_live_data.keys()))
443

    
444
    live_data = dict.fromkeys(node_names, {})
445
    live_data[live_data_name] = \
446
      dict((query._NODE_LIVE_FIELDS[name][2], value)
447
           for name, value in fake_live_data.items())
448

    
449
    node_to_primary = dict((name, set()) for name in node_names)
450
    node_to_primary[master_name].update(["inst1", "inst2"])
451

    
452
    node_to_secondary = dict((name, set()) for name in node_names)
453
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
454

    
455
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
456
    groups = {
457
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
458
      }
459

    
460
    oob_not_powered_node = node_names[0]
461
    nodes[0].powered = False
462
    oob_support = dict((name, False) for name in node_names)
463
    oob_support[master_name] = True
464
    oob_support[oob_not_powered_node] = True
465

    
466
    master_node.group = ng_uuid
467

    
468
    nqd = query.NodeQueryData(nodes, live_data, master_name,
469
                              node_to_primary, node_to_secondary, groups,
470
                              oob_support, cluster)
471
    result = q.Query(nqd)
472
    self.assert_(compat.all(len(row) == len(selected) for row in result))
473
    self.assertEqual([row[field_index["name"]] for row in result],
474
                     [(constants.RS_NORMAL, name) for name in node_names])
475

    
476
    node_to_row = dict((row[field_index["name"]][1], idx)
477
                       for idx, row in enumerate(result))
478

    
479
    master_row = result[node_to_row[master_name]]
480
    self.assert_(master_row[field_index["master"]])
481
    self.assert_(master_row[field_index["role"]], "M")
482
    self.assertEqual(master_row[field_index["group"]],
483
                     (constants.RS_NORMAL, "ng1"))
484
    self.assertEqual(master_row[field_index["group.uuid"]],
485
                     (constants.RS_NORMAL, ng_uuid))
486
    self.assertEqual(master_row[field_index["ctime"]],
487
                     (constants.RS_UNAVAIL, None))
488
    self.assertEqual(master_row[field_index["mtime"]],
489
                     (constants.RS_UNAVAIL, None))
490

    
491
    self.assert_(row[field_index["pip"]] == node.primary_ip and
492
                 row[field_index["sip"]] == node.secondary_ip and
493
                 set(row[field_index["tags"]]) == node.GetTags() and
494
                 row[field_index["serial_no"]] == node.serial_no and
495
                 row[field_index["role"]] == query._GetNodeRole(node,
496
                                                                master_name) and
497
                 (node.name == master_name or
498
                  (row[field_index["group"]] == "<unknown>" and
499
                   row[field_index["group.uuid"]] is None and
500
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
501
                                                 node.ctime) and
502
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
503
                                                 node.mtime) and
504
                   row[field_index["powered"]] == (constants.RS_NORMAL,
505
                                                   True))) or
506
                 (node.name == oob_not_powered_node and
507
                  row[field_index["powered"]] == (constants.RS_NORMAL,
508
                                                  False)) or
509
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
510
                 for row, node in zip(result, nodes))
511

    
512
    live_data_row = result[node_to_row[live_data_name]]
513

    
514
    for (field, value) in fake_live_data.items():
515
      self.assertEqual(live_data_row[field_index[field]],
516
                       (constants.RS_NORMAL, value))
517

    
518
    self.assertEqual(master_row[field_index["pinst_cnt"]],
519
                     (constants.RS_NORMAL, 2))
520
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
521
                     (constants.RS_NORMAL, 3))
522
    self.assertEqual(master_row[field_index["pinst_list"]],
523
                     (constants.RS_NORMAL,
524
                      list(node_to_primary[master_name])))
525
    self.assertEqual(live_data_row[field_index["sinst_list"]],
526
                     (constants.RS_NORMAL,
527
                      list(node_to_secondary[live_data_name])))
528

    
529
  def testGetLiveNodeField(self):
530
    nodes = [
531
      objects.Node(name="node1", drained=False, offline=False,
532
                   vm_capable=True),
533
      objects.Node(name="node2", drained=True, offline=False,
534
                   vm_capable=True),
535
      objects.Node(name="node3", drained=False, offline=False,
536
                   vm_capable=True),
537
      objects.Node(name="node4", drained=False, offline=True,
538
                   vm_capable=True),
539
      objects.Node(name="node5", drained=False, offline=False,
540
                   vm_capable=False),
541
      ]
542
    live_data = dict.fromkeys([node.name for node in nodes], {})
543

    
544
    # No data
545
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
546
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
547
                                             nqd, nodes[0]),
548
                     query._FS_NODATA)
549

    
550
    # Missing field
551
    ctx = _QueryData(None, curlive_data={
552
      "some": 1,
553
      "other": 2,
554
      })
555
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
556
                                             ctx, nodes[0]),
557
                     query._FS_UNAVAIL)
558

    
559
    # Wrong format/datatype
560
    ctx = _QueryData(None, curlive_data={
561
      "hello": ["Hello World"],
562
      "other": 2,
563
      })
564
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
565
                                             ctx, nodes[0]),
566
                     query._FS_UNAVAIL)
567

    
568
    # Offline node
569
    assert nodes[3].offline
570
    ctx = _QueryData(None, curlive_data={})
571
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
572
                                             ctx, nodes[3]),
573
                     query._FS_OFFLINE, None)
574

    
575
    # Wrong field type
576
    ctx = _QueryData(None, curlive_data={"hello": 123})
577
    self.assertRaises(AssertionError, query._GetLiveNodeField,
578
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
579

    
580
    # Non-vm_capable node
581
    assert not nodes[4].vm_capable
582
    ctx = _QueryData(None, curlive_data={})
583
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
584
                                             ctx, nodes[4]),
585
                     query._FS_UNAVAIL, None)
586

    
587

    
588
class TestInstanceQuery(unittest.TestCase):
589
  def _Create(self, selected):
590
    return query.Query(query.INSTANCE_FIELDS, selected)
591

    
592
  def testSimple(self):
593
    q = self._Create(["name", "be/maxmem", "ip"])
594
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
595

    
596
    cluster = objects.Cluster(cluster_name="testcluster",
597
      hvparams=constants.HVC_DEFAULTS,
598
      beparams={
599
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
600
        },
601
      nicparams={
602
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
603
        },
604
      os_hvp={},
605
      osparams={})
606

    
607
    instances = [
608
      objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
609
                       nics=[], os="deb1"),
610
      objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
611
        os="foomoo",
612
        beparams={
613
          constants.BE_MAXMEM: 512,
614
        }),
615
      objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
616
        os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
617
      ]
618

    
619
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
620
                                  set(), {}, None, None)
621
    self.assertEqual(q.Query(iqd),
622
      [[(constants.RS_NORMAL, "inst1"),
623
        (constants.RS_NORMAL, 128),
624
        (constants.RS_UNAVAIL, None),
625
       ],
626
       [(constants.RS_NORMAL, "inst2"),
627
        (constants.RS_NORMAL, 512),
628
        (constants.RS_UNAVAIL, None),
629
       ],
630
       [(constants.RS_NORMAL, "inst3"),
631
        (constants.RS_NORMAL, 128),
632
        (constants.RS_NORMAL, "192.0.2.99"),
633
       ]])
634
    self.assertEqual(q.OldStyleQuery(iqd),
635
      [["inst1", 128, None],
636
       ["inst2", 512, None],
637
       ["inst3", 128, "192.0.2.99"]])
638

    
639
  def test(self):
640
    selected = query.INSTANCE_FIELDS.keys()
641
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
642

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

    
646
    q = self._Create(selected)
647
    self.assertEqual(q.RequestedData(),
648
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
649
                          query.IQ_CONSOLE, query.IQ_NODES]))
650

    
651
    cluster = objects.Cluster(cluster_name="testcluster",
652
      hvparams=constants.HVC_DEFAULTS,
653
      beparams={
654
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
655
        },
656
      nicparams={
657
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
658
        },
659
      os_hvp={},
660
      tcpudp_port_pool=set(),
661
      osparams={
662
        "deb99": {
663
          "clean_install": "yes",
664
          },
665
        })
666

    
667
    offline_nodes = ["nodeoff1", "nodeoff2"]
668
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
669
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
670

    
671
    instances = [
672
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
673
        uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
674
        ctime=1291244000, mtime=1291244400, serial_no=30,
675
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_PVM,
676
        os="linux1",
677
        primary_node="node1",
678
        disk_template=constants.DT_PLAIN,
679
        disks=[],
680
        osparams={}),
681
      objects.Instance(name="inst2", hvparams={}, nics=[],
682
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
683
        ctime=1291211000, mtime=1291211077, serial_no=1,
684
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
685
        os="deb99",
686
        primary_node="node5",
687
        disk_template=constants.DT_DISKLESS,
688
        disks=[],
689
        beparams={
690
          constants.BE_MAXMEM: 512,
691
          constants.BE_MINMEM: 256,
692
        },
693
        osparams={}),
694
      objects.Instance(name="inst3", hvparams={}, beparams={},
695
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
696
        ctime=1291011000, mtime=1291013000, serial_no=1923,
697
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_KVM,
698
        os="busybox",
699
        primary_node="node6",
700
        disk_template=constants.DT_DRBD8,
701
        disks=[],
702
        nics=[
703
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
704
                      nicparams={
705
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
706
                        }),
707
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
708
          ],
709
        osparams={}),
710
      objects.Instance(name="inst4", hvparams={}, beparams={},
711
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
712
        ctime=1291244390, mtime=1291244395, serial_no=25,
713
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_PVM,
714
        os="linux1",
715
        primary_node="nodeoff2",
716
        disk_template=constants.DT_DRBD8,
717
        disks=[],
718
        nics=[
719
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
720
                      nicparams={
721
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
722
                        }),
723
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
724
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
725
                      nicparams={
726
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
727
                        }),
728
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
729
                      nicparams={
730
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
731
                        constants.NIC_LINK: "eth123",
732
                        }),
733
          ],
734
        osparams={}),
735
      objects.Instance(name="inst5", hvparams={}, nics=[],
736
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
737
        ctime=1231211000, mtime=1261200000, serial_no=3,
738
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
739
        os="deb99",
740
        primary_node="nodebad2",
741
        disk_template=constants.DT_DISKLESS,
742
        disks=[],
743
        beparams={
744
          constants.BE_MAXMEM: 512,
745
          constants.BE_MINMEM: 512,
746
        },
747
        osparams={}),
748
      objects.Instance(name="inst6", hvparams={}, nics=[],
749
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
750
        ctime=7513, mtime=11501, serial_no=13390,
751
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
752
        os="deb99",
753
        primary_node="node7",
754
        disk_template=constants.DT_DISKLESS,
755
        disks=[],
756
        beparams={
757
          constants.BE_MAXMEM: 768,
758
          constants.BE_MINMEM: 256,
759
        },
760
        osparams={
761
          "clean_install": "no",
762
          }),
763
      objects.Instance(name="inst7", hvparams={}, nics=[],
764
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
765
        ctime=None, mtime=None, serial_no=1947,
766
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
767
        os="deb99",
768
        primary_node="node6",
769
        disk_template=constants.DT_DISKLESS,
770
        disks=[],
771
        beparams={},
772
        osparams={}),
773
      objects.Instance(name="inst8", hvparams={}, nics=[],
774
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920f",
775
        ctime=None, mtime=None, serial_no=19478,
776
        admin_state=constants.ADMINST_OFFLINE, hypervisor=constants.HT_XEN_HVM,
777
        os="deb99",
778
        primary_node="node6",
779
        disk_template=constants.DT_DISKLESS,
780
        disks=[],
781
        beparams={},
782
        osparams={}),
783
      ]
784

    
785
    assert not utils.FindDuplicates(inst.name for inst in instances)
786

    
787
    instbyname = dict((inst.name, inst) for inst in instances)
788

    
789
    disk_usage = dict((inst.name,
790
                       gmi.ComputeDiskSize(inst.disk_template,
791
                                           [{"size": disk.size}
792
                                           for disk in inst.disks]))
793
                      for inst in instances)
794

    
795
    inst_bridges = {
796
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
797
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
798
                None, "eth123"],
799
      }
800

    
801
    live_data = {
802
      "inst2": {
803
        "vcpus": 3,
804
        },
805
      "inst4": {
806
        "memory": 123,
807
        },
808
      "inst6": {
809
        "memory": 768,
810
        },
811
      "inst7": {
812
        "vcpus": 3,
813
        },
814
      }
815
    wrongnode_inst = set(["inst7"])
816

    
817
    consinfo = dict((inst.name, None) for inst in instances)
818
    consinfo["inst7"] = \
819
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
820
                              host=instbyname["inst7"].primary_node,
821
                              user="root",
822
                              command=["hostname"]).ToDict()
823

    
824
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
825
                                  offline_nodes, bad_nodes, live_data,
826
                                  wrongnode_inst, consinfo, {}, {})
827
    result = q.Query(iqd)
828
    self.assertEqual(len(result), len(instances))
829
    self.assert_(compat.all(len(row) == len(selected)
830
                            for row in result))
831

    
832
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
833
           "Offline nodes not included in bad nodes"
834

    
835
    tested_status = set()
836

    
837
    for (inst, row) in zip(instances, result):
838
      assert inst.primary_node in nodes
839

    
840
      self.assertEqual(row[fieldidx["name"]],
841
                       (constants.RS_NORMAL, inst.name))
842

    
843
      if inst.primary_node in offline_nodes:
844
        exp_status = constants.INSTST_NODEOFFLINE
845
      elif inst.primary_node in bad_nodes:
846
        exp_status = constants.INSTST_NODEDOWN
847
      elif inst.name in live_data:
848
        if inst.name in wrongnode_inst:
849
          exp_status = constants.INSTST_WRONGNODE
850
        elif inst.admin_state == constants.ADMINST_UP:
851
          exp_status = constants.INSTST_RUNNING
852
        else:
853
          exp_status = constants.INSTST_ERRORUP
854
      elif inst.admin_state == constants.ADMINST_UP:
855
        exp_status = constants.INSTST_ERRORDOWN
856
      elif inst.admin_state == constants.ADMINST_DOWN:
857
        exp_status = constants.INSTST_ADMINDOWN
858
      else:
859
        exp_status = constants.INSTST_ADMINOFFLINE
860

    
861
      self.assertEqual(row[fieldidx["status"]],
862
                       (constants.RS_NORMAL, exp_status))
863

    
864
      (_, status) = row[fieldidx["status"]]
865
      tested_status.add(status)
866

    
867
      #FIXME(dynmem): check oper_ram vs min/max mem
868
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
869
        if inst.primary_node in bad_nodes:
870
          exp = (constants.RS_NODATA, None)
871
        elif inst.name in live_data:
872
          value = live_data[inst.name].get(livefield, None)
873
          if value is None:
874
            exp = (constants.RS_UNAVAIL, None)
875
          else:
876
            exp = (constants.RS_NORMAL, value)
877
        else:
878
          exp = (constants.RS_UNAVAIL, None)
879

    
880
        self.assertEqual(row[fieldidx[field]], exp)
881

    
882
      bridges = inst_bridges.get(inst.name, [])
883
      self.assertEqual(row[fieldidx["nic.bridges"]],
884
                       (constants.RS_NORMAL, bridges))
885
      if bridges:
886
        self.assertEqual(row[fieldidx["bridge"]],
887
                         (constants.RS_NORMAL, bridges[0]))
888
      else:
889
        self.assertEqual(row[fieldidx["bridge"]],
890
                         (constants.RS_UNAVAIL, None))
891

    
892
      for i in range(constants.MAX_NICS):
893
        if i < len(bridges) and bridges[i] is not None:
894
          exp = (constants.RS_NORMAL, bridges[i])
895
        else:
896
          exp = (constants.RS_UNAVAIL, None)
897
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
898

    
899
      if inst.primary_node in bad_nodes:
900
        exp = (constants.RS_NODATA, None)
901
      else:
902
        exp = (constants.RS_NORMAL, inst.name in live_data)
903
      self.assertEqual(row[fieldidx["oper_state"]], exp)
904

    
905
      cust_exp = (constants.RS_NORMAL, {})
906
      if inst.os == "deb99":
907
        if inst.name == "inst6":
908
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
909
          cust_exp = exp
910
        else:
911
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
912
      else:
913
        exp = (constants.RS_NORMAL, {})
914
      self.assertEqual(row[fieldidx["osparams"]], exp)
915
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
916

    
917
      usage = disk_usage[inst.name]
918
      if usage is None:
919
        usage = 0
920
      self.assertEqual(row[fieldidx["disk_usage"]],
921
                       (constants.RS_NORMAL, usage))
922

    
923
      for alias, target in [("sda_size", "disk.size/0"),
924
                            ("sdb_size", "disk.size/1"),
925
                            ("vcpus", "be/vcpus"),
926
                            ("ip", "nic.ip/0"),
927
                            ("mac", "nic.mac/0"),
928
                            ("bridge", "nic.bridge/0"),
929
                            ("nic_mode", "nic.mode/0"),
930
                            ("nic_link", "nic.link/0"),
931
                            ]:
932
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
933

    
934
      for field in ["ctime", "mtime"]:
935
        if getattr(inst, field) is None:
936
          # No ctime/mtime
937
          exp = (constants.RS_UNAVAIL, None)
938
        else:
939
          exp = (constants.RS_NORMAL, getattr(inst, field))
940
        self.assertEqual(row[fieldidx[field]], exp)
941

    
942
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
943

    
944
    # Ensure all possible status' have been tested
945
    self.assertEqual(tested_status, constants.INSTST_ALL)
946

    
947
  def _CheckInstanceConsole(self, instance, (status, consdata)):
948
    if instance.name == "inst7":
949
      self.assertEqual(status, constants.RS_NORMAL)
950
      console = objects.InstanceConsole.FromDict(consdata)
951
      self.assertTrue(console.Validate())
952
      self.assertEqual(console.host, instance.primary_node)
953
    else:
954
      self.assertEqual(status, constants.RS_UNAVAIL)
955

    
956

    
957
class TestGroupQuery(unittest.TestCase):
958

    
959
  def setUp(self):
960
    self.custom_diskparams = {
961
      constants.DT_DRBD8: {
962
        constants.DRBD_DEFAULT_METAVG: "foobar",
963
      },
964
    }
965

    
966
    self.groups = [
967
      objects.NodeGroup(name="default",
968
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
969
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED,
970
                        ipolicy=objects.MakeEmptyIPolicy(),
971
                        ndparams={},
972
                        diskparams={},
973
                        ),
974
      objects.NodeGroup(name="restricted",
975
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
976
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
977
                        ipolicy=objects.MakeEmptyIPolicy(),
978
                        ndparams={},
979
                        diskparams=self.custom_diskparams,
980
                        ),
981
      ]
982
    self.cluster = objects.Cluster(cluster_name="testcluster",
983
      hvparams=constants.HVC_DEFAULTS,
984
      beparams={
985
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
986
        },
987
      nicparams={
988
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
989
        },
990
      ndparams=constants.NDC_DEFAULTS,
991
      ipolicy=constants.IPOLICY_DEFAULTS,
992
      diskparams=constants.DISK_DT_DEFAULTS,
993
      )
994

    
995
  def _Create(self, selected):
996
    return query.Query(query.GROUP_FIELDS, selected)
997

    
998
  def testSimple(self):
999
    q = self._Create(["name", "uuid", "alloc_policy"])
1000
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1001

    
1002
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1003

    
1004
    self.assertEqual(q.Query(gqd),
1005
      [[(constants.RS_NORMAL, "default"),
1006
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1007
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1008
        ],
1009
       [(constants.RS_NORMAL, "restricted"),
1010
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1011
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1012
        ],
1013
       ])
1014

    
1015
  def testNodes(self):
1016
    groups_to_nodes = {
1017
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1018
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1019
      }
1020

    
1021
    q = self._Create(["name", "node_cnt", "node_list"])
1022
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1023
                               False)
1024

    
1025
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1026

    
1027
    self.assertEqual(q.Query(gqd),
1028
                     [[(constants.RS_NORMAL, "default"),
1029
                       (constants.RS_NORMAL, 2),
1030
                       (constants.RS_NORMAL, ["node1", "node2"]),
1031
                       ],
1032
                      [(constants.RS_NORMAL, "restricted"),
1033
                       (constants.RS_NORMAL, 3),
1034
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1035
                       ],
1036
                      ])
1037

    
1038
  def testInstances(self):
1039
    groups_to_instances = {
1040
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1041
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1042
      }
1043

    
1044
    q = self._Create(["pinst_cnt", "pinst_list"])
1045
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1046
      groups_to_instances, False)
1047

    
1048
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1049

    
1050
    self.assertEqual(q.Query(gqd),
1051
                     [[(constants.RS_NORMAL, 2),
1052
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1053
                       ],
1054
                      [(constants.RS_NORMAL, 3),
1055
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1056
                       ],
1057
                      ])
1058

    
1059
  def testDiskparams(self):
1060
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1061
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1062

    
1063
    self.assertEqual(q.RequestedData(),
1064
                     set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1065

    
1066
    self.assertEqual(q.Query(gqd),
1067
      [[(constants.RS_NORMAL, "default"),
1068
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1069
        (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1070
        (constants.RS_NORMAL, {}),
1071
        ],
1072
       [(constants.RS_NORMAL, "restricted"),
1073
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1074
        (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1075
                                                     self.custom_diskparams)),
1076
        (constants.RS_NORMAL, self.custom_diskparams),
1077
        ],
1078
       ])
1079

    
1080

    
1081
class TestOsQuery(unittest.TestCase):
1082
  def _Create(self, selected):
1083
    return query.Query(query.OS_FIELDS, selected)
1084

    
1085
  def test(self):
1086
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1087
    api_versions = [10, 0, 15, 5]
1088
    parameters = ["zpar3", "apar9"]
1089

    
1090
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1091
    assert (api_versions != sorted(api_versions) and
1092
            api_versions != utils.NiceSort(variants))
1093
    assert (parameters != sorted(parameters) and
1094
            parameters != utils.NiceSort(parameters))
1095

    
1096
    data = [
1097
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1098
                   variants=set(), api_versions=set(), parameters=set(),
1099
                   node_status={ "some": "status", }),
1100
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1101
                   variants=set(variants),
1102
                   api_versions=set(api_versions),
1103
                   parameters=set(parameters),
1104
                   node_status={ "some": "other", "status": None, }),
1105
      ]
1106

    
1107

    
1108
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1109
                      "api_versions", "parameters", "node_status"])
1110
    self.assertEqual(q.RequestedData(), set([]))
1111
    self.assertEqual(q.Query(data),
1112
                     [[(constants.RS_NORMAL, "debian"),
1113
                       (constants.RS_NORMAL, False),
1114
                       (constants.RS_NORMAL, False),
1115
                       (constants.RS_NORMAL, False),
1116
                       (constants.RS_NORMAL, []),
1117
                       (constants.RS_NORMAL, []),
1118
                       (constants.RS_NORMAL, []),
1119
                       (constants.RS_NORMAL, {"some": "status"})],
1120
                      [(constants.RS_NORMAL, "dos"),
1121
                       (constants.RS_NORMAL, True),
1122
                       (constants.RS_NORMAL, False),
1123
                       (constants.RS_NORMAL, True),
1124
                       (constants.RS_NORMAL,
1125
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1126
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1127
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1128
                       (constants.RS_NORMAL,
1129
                        { "some": "other", "status": None, })
1130
                       ]])
1131

    
1132

    
1133
class TestQueryFields(unittest.TestCase):
1134
  def testAllFields(self):
1135
    for fielddefs in query.ALL_FIELD_LISTS:
1136
      result = query.QueryFields(fielddefs, None)
1137
      self.assert_(isinstance(result, dict))
1138
      response = objects.QueryFieldsResponse.FromDict(result)
1139
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1140
        [(fdef2.name, fdef2.title)
1141
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1142
                                                key=lambda x: x[0].name)])
1143

    
1144
  def testSomeFields(self):
1145
    rnd = random.Random(5357)
1146

    
1147
    for _ in range(10):
1148
      for fielddefs in query.ALL_FIELD_LISTS:
1149
        if len(fielddefs) > 20:
1150
          sample_size = rnd.randint(5, 20)
1151
        else:
1152
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1153
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1154
                                                         sample_size)]
1155
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1156
        self.assert_(isinstance(result, dict))
1157
        response = objects.QueryFieldsResponse.FromDict(result)
1158
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1159
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1160

    
1161

    
1162
class TestQueryFilter(unittest.TestCase):
1163
  def testRequestedNames(self):
1164
    for (what, fielddefs) in query.ALL_FIELDS.items():
1165
      if what == constants.QR_JOB:
1166
        namefield = "id"
1167
        nameval = 123
1168
        namevalempty = 0
1169
        genval = lambda i: i * 10
1170
        randvals = [17361, 22015, 13193, 15215]
1171
      else:
1172
        nameval = "abc"
1173
        namevalempty = ""
1174
        genval = lambda i: "x%s" % i
1175
        randvals = ["x17361", "x22015", "x13193", "x15215"]
1176
        if what == constants.QR_EXPORT:
1177
          namefield = "export"
1178
        else:
1179
          namefield = "name"
1180

    
1181
      assert namefield in fielddefs
1182

    
1183
      reqnames = [genval(i) for i in range(4)]
1184
      innerfilter = [["=", namefield, v] for v in reqnames]
1185

    
1186
      # No name field
1187
      q = query.Query(fielddefs, [namefield],
1188
                      qfilter=["=", namefield, nameval], namefield=None)
1189
      self.assertEqual(q.RequestedNames(), None)
1190

    
1191
      # No filter
1192
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1193
      self.assertEqual(q.RequestedNames(), None)
1194

    
1195
      # Check empty query
1196
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1197
                      namefield=namefield)
1198
      self.assertEqual(q.RequestedNames(), None)
1199

    
1200
      # Check order
1201
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1202
                      namefield=namefield)
1203
      self.assertEqual(q.RequestedNames(), reqnames)
1204

    
1205
      # Check reverse order
1206
      q = query.Query(fielddefs, [namefield],
1207
                      qfilter=["|"] + list(reversed(innerfilter)),
1208
                      namefield=namefield)
1209
      self.assertEqual(q.RequestedNames(), list(reversed(reqnames)))
1210

    
1211
      # Duplicates
1212
      q = query.Query(fielddefs, [namefield],
1213
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1214
                      namefield=namefield)
1215
      self.assertEqual(q.RequestedNames(), reqnames)
1216

    
1217
      # Unknown name field
1218
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1219
                        namefield="_unknown_field_")
1220

    
1221
      # Filter with AND
1222
      q = query.Query(fielddefs, [namefield],
1223
                      qfilter=["|", ["=", namefield, nameval],
1224
                                    ["&", ["=", namefield, namevalempty]]],
1225
                      namefield=namefield)
1226
      self.assertTrue(q.RequestedNames() is None)
1227

    
1228
      # Filter with NOT
1229
      q = query.Query(fielddefs, [namefield],
1230
                      qfilter=["|", ["=", namefield, nameval],
1231
                                    ["!", ["=", namefield, namevalempty]]],
1232
                      namefield=namefield)
1233
      self.assertTrue(q.RequestedNames() is None)
1234

    
1235
      # Filter with only OR (names must be in correct order)
1236
      q = query.Query(fielddefs, [namefield],
1237
                      qfilter=["|", ["=", namefield, randvals[0]],
1238
                                    ["|", ["=", namefield, randvals[1]]],
1239
                                    ["|", ["|", ["=", namefield, randvals[2]]]],
1240
                                    ["=", namefield, randvals[3]]],
1241
                      namefield=namefield)
1242
      self.assertEqual(q.RequestedNames(), randvals)
1243

    
1244
  @staticmethod
1245
  def _GenNestedFilter(namefield, op, depth, nameval):
1246
    nested = ["=", namefield, nameval]
1247
    for i in range(depth):
1248
      nested = [op, nested]
1249
    return nested
1250

    
1251
  def testCompileFilter(self):
1252
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1253

    
1254
    for (what, fielddefs) in query.ALL_FIELDS.items():
1255
      if what == constants.QR_JOB:
1256
        namefield = "id"
1257
        nameval = 123
1258
      elif what == constants.QR_EXPORT:
1259
        namefield = "export"
1260
        nameval = "value"
1261
      else:
1262
        namefield = "name"
1263
        nameval = "value"
1264

    
1265
      checks = [
1266
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1267
        ["=", "_unknown_field", "value"],
1268
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1269
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1270
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1271
        ]
1272

    
1273
      for qfilter in checks:
1274
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1275
                          fielddefs, None, qfilter)
1276

    
1277
      for op in ["|", "!"]:
1278
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1279
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1280
                                                      qfilter)))
1281

    
1282
  def testQueryInputOrder(self):
1283
    fielddefs = query._PrepareFieldList([
1284
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1285
       None, 0, lambda ctx, item: item["pnode"]),
1286
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1287
       None, 0, lambda ctx, item: item["snode"]),
1288
      ], [])
1289

    
1290
    data = [
1291
      { "pnode": "node1", "snode": "node44", },
1292
      { "pnode": "node30", "snode": "node90", },
1293
      { "pnode": "node25", "snode": "node1", },
1294
      { "pnode": "node20", "snode": "node1", },
1295
      ]
1296

    
1297
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1298

    
1299
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1300
                    qfilter=qfilter)
1301
    self.assertTrue(q.RequestedNames() is None)
1302
    self.assertFalse(q.RequestedData())
1303
    self.assertEqual(q.Query(data),
1304
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1305
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1306
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1307

    
1308
    # Try again with reversed input data
1309
    self.assertEqual(q.Query(reversed(data)),
1310
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1311
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1312
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1313

    
1314
    # No name field, result must be in incoming order
1315
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1316
                    qfilter=qfilter)
1317
    self.assertFalse(q.RequestedData())
1318
    self.assertEqual(q.Query(data),
1319
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1320
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1321
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1322
    self.assertEqual(q.OldStyleQuery(data), [
1323
      ["node1", "node44"],
1324
      ["node25", "node1"],
1325
      ["node20", "node1"],
1326
      ])
1327
    self.assertEqual(q.Query(reversed(data)),
1328
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1329
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1330
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1331
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1332
      ["node20", "node1"],
1333
      ["node25", "node1"],
1334
      ["node1", "node44"],
1335
      ])
1336

    
1337
    # Name field, but no sorting, result must be in incoming order
1338
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1339
    self.assertFalse(q.RequestedData())
1340
    self.assertEqual(q.Query(data, sort_by_name=False),
1341
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1342
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1343
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1344
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1345
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1346
      ["node1", "node44"],
1347
      ["node30", "node90"],
1348
      ["node25", "node1"],
1349
      ["node20", "node1"],
1350
      ])
1351
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1352
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1353
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1354
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1355
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1356
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1357
      ["node20", "node1"],
1358
      ["node25", "node1"],
1359
      ["node30", "node90"],
1360
      ["node1", "node44"],
1361
      ])
1362

    
1363
  def testEqualNamesOrder(self):
1364
    fielddefs = query._PrepareFieldList([
1365
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1366
       None, 0, lambda ctx, item: item["pnode"]),
1367
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1368
       None, 0, lambda ctx, item: item["num"]),
1369
      ], [])
1370

    
1371
    data = [
1372
      { "pnode": "node1", "num": 100, },
1373
      { "pnode": "node1", "num": 25, },
1374
      { "pnode": "node2", "num": 90, },
1375
      { "pnode": "node2", "num": 30, },
1376
      ]
1377

    
1378
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1379
                    qfilter=["|", ["=", "pnode", "node1"],
1380
                                  ["=", "pnode", "node2"],
1381
                                  ["=", "pnode", "node1"]])
1382
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1383
                     msg="Did not return unique names")
1384
    self.assertFalse(q.RequestedData())
1385
    self.assertEqual(q.Query(data),
1386
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1387
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1388
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1389
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1390
    self.assertEqual(q.Query(data, sort_by_name=False),
1391
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1392
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1393
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1394
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1395

    
1396
    data = [
1397
      { "pnode": "nodeX", "num": 50, },
1398
      { "pnode": "nodeY", "num": 40, },
1399
      { "pnode": "nodeX", "num": 30, },
1400
      { "pnode": "nodeX", "num": 20, },
1401
      { "pnode": "nodeM", "num": 10, },
1402
      ]
1403

    
1404
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1405
                    qfilter=["|", ["=", "pnode", "nodeX"],
1406
                                  ["=", "pnode", "nodeY"],
1407
                                  ["=", "pnode", "nodeY"],
1408
                                  ["=", "pnode", "nodeY"],
1409
                                  ["=", "pnode", "nodeM"]])
1410
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1411
                     msg="Did not return unique names")
1412
    self.assertFalse(q.RequestedData())
1413

    
1414
    # First sorted by name, then input order
1415
    self.assertEqual(q.Query(data, sort_by_name=True),
1416
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1417
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1418
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1419
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1420
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1421

    
1422
    # Input order
1423
    self.assertEqual(q.Query(data, sort_by_name=False),
1424
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1425
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1426
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1427
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1428
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1429

    
1430
  def testFilter(self):
1431
    (DK_A, DK_B) = range(1000, 1002)
1432

    
1433
    fielddefs = query._PrepareFieldList([
1434
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1435
       DK_A, 0, lambda ctx, item: item["name"]),
1436
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1437
       DK_B, 0, lambda ctx, item: item["other"]),
1438
      ], [])
1439

    
1440
    data = [
1441
      { "name": "node1", "other": "foo", },
1442
      { "name": "node2", "other": "bar", },
1443
      { "name": "node3", "other": "Hello", },
1444
      ]
1445

    
1446
    # Empty filter
1447
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1448
                    qfilter=["|"])
1449
    self.assertTrue(q.RequestedNames() is None)
1450
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1451
    self.assertEqual(q.Query(data), [])
1452

    
1453
    # Normal filter
1454
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1455
                    qfilter=["=", "name", "node1"])
1456
    self.assertEqual(q.RequestedNames(), ["node1"])
1457
    self.assertEqual(q.Query(data),
1458
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1459

    
1460
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1461
                    qfilter=(["|", ["=", "name", "node1"],
1462
                                   ["=", "name", "node3"]]))
1463
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1464
    self.assertEqual(q.Query(data),
1465
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1466
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1467

    
1468
    # Complex filter
1469
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1470
                    qfilter=(["|", ["=", "name", "node1"],
1471
                                   ["|", ["=", "name", "node3"],
1472
                                         ["=", "name", "node2"]],
1473
                                   ["=", "name", "node3"]]))
1474
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1475
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1476
    self.assertEqual(q.Query(data),
1477
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1478
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1479
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1480

    
1481
    # Filter data type mismatch
1482
    for i in [-1, 0, 1, 123, [], None, True, False]:
1483
      self.assertRaises(errors.ParameterError, query.Query,
1484
                        fielddefs, ["name", "other"], namefield="name",
1485
                        qfilter=["=", "name", i])
1486

    
1487
    # Negative filter
1488
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1489
                    qfilter=["!", ["|", ["=", "name", "node1"],
1490
                                        ["=", "name", "node3"]]])
1491
    self.assertTrue(q.RequestedNames() is None)
1492
    self.assertEqual(q.Query(data),
1493
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1494

    
1495
    # Not equal
1496
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1497
                    qfilter=["!=", "name", "node3"])
1498
    self.assertTrue(q.RequestedNames() is None)
1499
    self.assertEqual(q.Query(data),
1500
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1501
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1502

    
1503
    # Data type
1504
    q = query.Query(fielddefs, [], namefield="name",
1505
                    qfilter=["|", ["=", "other", "bar"],
1506
                                  ["=", "name", "foo"]])
1507
    self.assertTrue(q.RequestedNames() is None)
1508
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1509
    self.assertEqual(q.Query(data), [[]])
1510

    
1511
    # Only one data type
1512
    q = query.Query(fielddefs, ["other"], namefield="name",
1513
                    qfilter=["=", "other", "bar"])
1514
    self.assertTrue(q.RequestedNames() is None)
1515
    self.assertEqual(q.RequestedData(), set([DK_B]))
1516
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1517

    
1518
    q = query.Query(fielddefs, [], namefield="name",
1519
                    qfilter=["=", "other", "bar"])
1520
    self.assertTrue(q.RequestedNames() is None)
1521
    self.assertEqual(q.RequestedData(), set([DK_B]))
1522
    self.assertEqual(q.Query(data), [[]])
1523

    
1524
    # Data type in boolean operator
1525
    q = query.Query(fielddefs, [], namefield="name",
1526
                    qfilter=["?", "name"])
1527
    self.assertTrue(q.RequestedNames() is None)
1528
    self.assertEqual(q.RequestedData(), set([DK_A]))
1529
    self.assertEqual(q.Query(data), [[], [], []])
1530

    
1531
    q = query.Query(fielddefs, [], namefield="name",
1532
                    qfilter=["!", ["?", "name"]])
1533
    self.assertTrue(q.RequestedNames() is None)
1534
    self.assertEqual(q.RequestedData(), set([DK_A]))
1535
    self.assertEqual(q.Query(data), [])
1536

    
1537
  def testFilterContains(self):
1538
    fielddefs = query._PrepareFieldList([
1539
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1540
       None, 0, lambda ctx, item: item["name"]),
1541
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1542
       None, 0, lambda ctx, item: item["other"]),
1543
      ], [])
1544

    
1545
    data = [
1546
      { "name": "node2", "other": ["x", "y", "bar"], },
1547
      { "name": "node3", "other": "Hello", },
1548
      { "name": "node1", "other": ["a", "b", "foo"], },
1549
      { "name": "empty", "other": []},
1550
      ]
1551

    
1552
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1553
                    qfilter=["=[]", "other", "bar"])
1554
    self.assertTrue(q.RequestedNames() is None)
1555
    self.assertEqual(q.Query(data), [
1556
      [(constants.RS_NORMAL, "node2"),
1557
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1558
      ])
1559

    
1560
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1561
                    qfilter=["|", ["=[]", "other", "bar"],
1562
                                  ["=[]", "other", "a"],
1563
                                  ["=[]", "other", "b"]])
1564
    self.assertTrue(q.RequestedNames() is None)
1565
    self.assertEqual(q.Query(data), [
1566
      [(constants.RS_NORMAL, "node1"),
1567
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1568
      [(constants.RS_NORMAL, "node2"),
1569
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1570
      ])
1571
    self.assertEqual(q.OldStyleQuery(data), [
1572
      ["node1", ["a", "b", "foo"]],
1573
      ["node2", ["x", "y", "bar"]],
1574
      ])
1575

    
1576
    # Boolean test
1577
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1578
                    qfilter=["?", "other"])
1579
    self.assertEqual(q.OldStyleQuery(data), [
1580
      ["node1", ["a", "b", "foo"]],
1581
      ["node2", ["x", "y", "bar"]],
1582
      ["node3", "Hello"],
1583
      ])
1584

    
1585
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1586
                    qfilter=["!", ["?", "other"]])
1587
    self.assertEqual(q.OldStyleQuery(data), [
1588
      ["empty", []],
1589
      ])
1590

    
1591
  def testFilterHostname(self):
1592
    fielddefs = query._PrepareFieldList([
1593
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1594
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1595
      ], [])
1596

    
1597
    data = [
1598
      { "name": "node1.example.com", },
1599
      { "name": "node2.example.com", },
1600
      { "name": "node2.example.net", },
1601
      ]
1602

    
1603
    q = query.Query(fielddefs, ["name"], namefield="name",
1604
                    qfilter=["=", "name", "node2"])
1605
    self.assertEqual(q.RequestedNames(), ["node2"])
1606
    self.assertEqual(q.Query(data), [
1607
      [(constants.RS_NORMAL, "node2.example.com")],
1608
      [(constants.RS_NORMAL, "node2.example.net")],
1609
      ])
1610

    
1611
    q = query.Query(fielddefs, ["name"], namefield="name",
1612
                    qfilter=["=", "name", "node1"])
1613
    self.assertEqual(q.RequestedNames(), ["node1"])
1614
    self.assertEqual(q.Query(data), [
1615
      [(constants.RS_NORMAL, "node1.example.com")],
1616
      ])
1617

    
1618
    q = query.Query(fielddefs, ["name"], namefield="name",
1619
                    qfilter=["=", "name", "othername"])
1620
    self.assertEqual(q.RequestedNames(), ["othername"])
1621
    self.assertEqual(q.Query(data), [])
1622

    
1623
    q = query.Query(fielddefs, ["name"], namefield="name",
1624
                    qfilter=["|", ["=", "name", "node1.example.com"],
1625
                                  ["=", "name", "node2"]])
1626
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1627
    self.assertEqual(q.Query(data), [
1628
      [(constants.RS_NORMAL, "node1.example.com")],
1629
      [(constants.RS_NORMAL, "node2.example.com")],
1630
      [(constants.RS_NORMAL, "node2.example.net")],
1631
      ])
1632
    self.assertEqual(q.OldStyleQuery(data), [
1633
      ["node1.example.com"],
1634
      ["node2.example.com"],
1635
      ["node2.example.net"],
1636
      ])
1637

    
1638
    q = query.Query(fielddefs, ["name"], namefield="name",
1639
                    qfilter=["!=", "name", "node1"])
1640
    self.assertTrue(q.RequestedNames() is None)
1641
    self.assertEqual(q.Query(data), [
1642
      [(constants.RS_NORMAL, "node2.example.com")],
1643
      [(constants.RS_NORMAL, "node2.example.net")],
1644
      ])
1645
    self.assertEqual(q.OldStyleQuery(data), [
1646
      ["node2.example.com"],
1647
      ["node2.example.net"],
1648
      ])
1649

    
1650
  def testFilterBoolean(self):
1651
    fielddefs = query._PrepareFieldList([
1652
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1653
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1654
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1655
       None, 0, lambda ctx, item: item["value"]),
1656
      ], [])
1657

    
1658
    data = [
1659
      { "name": "node1", "value": False, },
1660
      { "name": "node2", "value": True, },
1661
      { "name": "node3", "value": True, },
1662
      ]
1663

    
1664
    q = query.Query(fielddefs, ["name", "value"],
1665
                    qfilter=["|", ["=", "value", False],
1666
                                  ["=", "value", True]])
1667
    self.assertTrue(q.RequestedNames() is None)
1668
    self.assertEqual(q.Query(data), [
1669
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1670
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1671
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1672
      ])
1673

    
1674
    q = query.Query(fielddefs, ["name", "value"],
1675
                    qfilter=["|", ["=", "value", False],
1676
                                  ["!", ["=", "value", False]]])
1677
    self.assertTrue(q.RequestedNames() is None)
1678
    self.assertEqual(q.Query(data), [
1679
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1680
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1681
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1682
      ])
1683

    
1684
    # Comparing bool with string
1685
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1686
      self.assertRaises(errors.ParameterError, query.Query,
1687
                        fielddefs, ["name", "value"],
1688
                        qfilter=["=", "value", i])
1689

    
1690
    # Truth filter
1691
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1692
    self.assertTrue(q.RequestedNames() is None)
1693
    self.assertEqual(q.Query(data), [
1694
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1695
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1696
      ])
1697

    
1698
    # Negative bool filter
1699
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1700
    self.assertTrue(q.RequestedNames() is None)
1701
    self.assertEqual(q.Query(data), [
1702
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1703
      ])
1704

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

    
1717
  def testFilterRegex(self):
1718
    fielddefs = query._PrepareFieldList([
1719
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1720
       None, 0, lambda ctx, item: item["name"]),
1721
      ], [])
1722

    
1723
    data = [
1724
      { "name": "node1.example.com", },
1725
      { "name": "node2.site.example.com", },
1726
      { "name": "node2.example.net", },
1727

    
1728
      # Empty name
1729
      { "name": "", },
1730
      ]
1731

    
1732
    q = query.Query(fielddefs, ["name"], namefield="name",
1733
                    qfilter=["=~", "name", "site"])
1734
    self.assertTrue(q.RequestedNames() is None)
1735
    self.assertEqual(q.Query(data), [
1736
      [(constants.RS_NORMAL, "node2.site.example.com")],
1737
      ])
1738

    
1739
    q = query.Query(fielddefs, ["name"], namefield="name",
1740
                    qfilter=["=~", "name", "^node2"])
1741
    self.assertTrue(q.RequestedNames() is None)
1742
    self.assertEqual(q.Query(data), [
1743
      [(constants.RS_NORMAL, "node2.example.net")],
1744
      [(constants.RS_NORMAL, "node2.site.example.com")],
1745
      ])
1746

    
1747
    q = query.Query(fielddefs, ["name"], namefield="name",
1748
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1749
    self.assertTrue(q.RequestedNames() is None)
1750
    self.assertEqual(q.Query(data), [
1751
      [(constants.RS_NORMAL, "node1.example.com")],
1752
      [(constants.RS_NORMAL, "node2.site.example.com")],
1753
      ])
1754

    
1755
    q = query.Query(fielddefs, ["name"], namefield="name",
1756
                    qfilter=["=~", "name", r"."])
1757
    self.assertTrue(q.RequestedNames() is None)
1758
    self.assertEqual(q.Query(data), [
1759
      [(constants.RS_NORMAL, "node1.example.com")],
1760
      [(constants.RS_NORMAL, "node2.example.net")],
1761
      [(constants.RS_NORMAL, "node2.site.example.com")],
1762
      ])
1763

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

    
1771
    # Invalid regular expression
1772
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1773
                      qfilter=["=~", "name", r"["])
1774

    
1775
  def testFilterLessGreater(self):
1776
    fielddefs = query._PrepareFieldList([
1777
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1778
       None, 0, lambda ctx, item: item),
1779
      ], [])
1780

    
1781
    data = range(100)
1782

    
1783
    q = query.Query(fielddefs, ["value"],
1784
                    qfilter=["<", "value", 20])
1785
    self.assertTrue(q.RequestedNames() is None)
1786
    self.assertEqual(q.Query(data),
1787
                     [[(constants.RS_NORMAL, i)] for i in range(20)])
1788

    
1789
    q = query.Query(fielddefs, ["value"],
1790
                    qfilter=["<=", "value", 30])
1791
    self.assertTrue(q.RequestedNames() is None)
1792
    self.assertEqual(q.Query(data),
1793
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1794

    
1795
    q = query.Query(fielddefs, ["value"],
1796
                    qfilter=[">", "value", 40])
1797
    self.assertTrue(q.RequestedNames() is None)
1798
    self.assertEqual(q.Query(data),
1799
                     [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
1800

    
1801
    q = query.Query(fielddefs, ["value"],
1802
                    qfilter=[">=", "value", 50])
1803
    self.assertTrue(q.RequestedNames() is None)
1804
    self.assertEqual(q.Query(data),
1805
                     [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
1806

    
1807
  def testFilterLessGreaterJobId(self):
1808
    fielddefs = query._PrepareFieldList([
1809
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1810
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1811
      ], [])
1812

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

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

    
1817
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1818
    self.assertTrue(q.RequestedNames() is None)
1819
    self.assertEqual(q.Query(data), [
1820
      [(constants.RS_NORMAL, "1")],
1821
      [(constants.RS_NORMAL, "2")],
1822
      [(constants.RS_NORMAL, "3")],
1823
      [(constants.RS_NORMAL, "10")],
1824
      [(constants.RS_NORMAL, "15")],
1825
      [(constants.RS_NORMAL, "7")],
1826
      ])
1827

    
1828
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1829
    self.assertTrue(q.RequestedNames() is None)
1830
    self.assertEqual(q.Query(data), [
1831
      [(constants.RS_NORMAL, "102")],
1832
      [(constants.RS_NORMAL, "120")],
1833
      [(constants.RS_NORMAL, "125")],
1834
      [(constants.RS_NORMAL, "100")],
1835
      ])
1836

    
1837
    # Integers are no valid job IDs
1838
    self.assertRaises(errors.ParameterError, query.Query,
1839
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1840

    
1841
  def testFilterLessGreaterSplitTimestamp(self):
1842
    fielddefs = query._PrepareFieldList([
1843
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1844
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1845
      ], [])
1846

    
1847
    data = [
1848
      utils.SplitTime(0),
1849
      utils.SplitTime(0.1),
1850
      utils.SplitTime(18224.7872),
1851
      utils.SplitTime(919896.12623),
1852
      utils.SplitTime(999),
1853
      utils.SplitTime(989.9999),
1854
      ]
1855

    
1856
    for i in [0, [0, 0]]:
1857
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1858
      self.assertTrue(q.RequestedNames() is None)
1859
      self.assertEqual(q.Query(data), [])
1860

    
1861
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1862
    self.assertTrue(q.RequestedNames() is None)
1863
    self.assertEqual(q.Query(data), [
1864
      [(constants.RS_NORMAL, (0, 0))],
1865
      [(constants.RS_NORMAL, (0, 100000))],
1866
      [(constants.RS_NORMAL, (999, 0))],
1867
      [(constants.RS_NORMAL, (989, 999900))],
1868
      ])
1869

    
1870
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
1871
    self.assertTrue(q.RequestedNames() is None)
1872
    self.assertEqual(q.Query(data), [
1873
      [(constants.RS_NORMAL, (18224, 787200))],
1874
      [(constants.RS_NORMAL, (919896, 126230))],
1875
      ])
1876

    
1877
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1878
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1879
      self.assertTrue(q.RequestedNames() is None)
1880
      self.assertEqual(q.Query(data), [
1881
        [(constants.RS_NORMAL, (18224, 787200))],
1882
        [(constants.RS_NORMAL, (919896, 126230))],
1883
        ])
1884

    
1885
    q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880])
1886
    self.assertTrue(q.RequestedNames() is None)
1887
    self.assertEqual(q.Query(data), [
1888
      [(constants.RS_NORMAL, (919896, 126230))],
1889
      ])
1890

    
1891

    
1892
if __name__ == "__main__":
1893
  testutils.GanetiTestProgram()