Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 052783ff

History | View | Annotate | Download (72.8 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
  def testFilterContains(self):
1525
    fielddefs = query._PrepareFieldList([
1526
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1527
       None, 0, lambda ctx, item: item["name"]),
1528
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1529
       None, 0, lambda ctx, item: item["other"]),
1530
      ], [])
1531

    
1532
    data = [
1533
      { "name": "node2", "other": ["x", "y", "bar"], },
1534
      { "name": "node3", "other": "Hello", },
1535
      { "name": "node1", "other": ["a", "b", "foo"], },
1536
      { "name": "empty", "other": []},
1537
      ]
1538

    
1539
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1540
                    qfilter=["=[]", "other", "bar"])
1541
    self.assertTrue(q.RequestedNames() is None)
1542
    self.assertEqual(q.Query(data), [
1543
      [(constants.RS_NORMAL, "node2"),
1544
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1545
      ])
1546

    
1547
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1548
                    qfilter=["|", ["=[]", "other", "bar"],
1549
                                  ["=[]", "other", "a"],
1550
                                  ["=[]", "other", "b"]])
1551
    self.assertTrue(q.RequestedNames() is None)
1552
    self.assertEqual(q.Query(data), [
1553
      [(constants.RS_NORMAL, "node1"),
1554
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1555
      [(constants.RS_NORMAL, "node2"),
1556
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1557
      ])
1558
    self.assertEqual(q.OldStyleQuery(data), [
1559
      ["node1", ["a", "b", "foo"]],
1560
      ["node2", ["x", "y", "bar"]],
1561
      ])
1562

    
1563
    # Boolean test
1564
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1565
                    qfilter=["?", "other"])
1566
    self.assertEqual(q.OldStyleQuery(data), [
1567
      ["node1", ["a", "b", "foo"]],
1568
      ["node2", ["x", "y", "bar"]],
1569
      ["node3", "Hello"],
1570
      ])
1571

    
1572
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1573
                    qfilter=["!", ["?", "other"]])
1574
    self.assertEqual(q.OldStyleQuery(data), [
1575
      ["empty", []],
1576
      ])
1577

    
1578
  def testFilterHostname(self):
1579
    fielddefs = query._PrepareFieldList([
1580
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1581
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1582
      ], [])
1583

    
1584
    data = [
1585
      { "name": "node1.example.com", },
1586
      { "name": "node2.example.com", },
1587
      { "name": "node2.example.net", },
1588
      ]
1589

    
1590
    q = query.Query(fielddefs, ["name"], namefield="name",
1591
                    qfilter=["=", "name", "node2"])
1592
    self.assertEqual(q.RequestedNames(), ["node2"])
1593
    self.assertEqual(q.Query(data), [
1594
      [(constants.RS_NORMAL, "node2.example.com")],
1595
      [(constants.RS_NORMAL, "node2.example.net")],
1596
      ])
1597

    
1598
    q = query.Query(fielddefs, ["name"], namefield="name",
1599
                    qfilter=["=", "name", "node1"])
1600
    self.assertEqual(q.RequestedNames(), ["node1"])
1601
    self.assertEqual(q.Query(data), [
1602
      [(constants.RS_NORMAL, "node1.example.com")],
1603
      ])
1604

    
1605
    q = query.Query(fielddefs, ["name"], namefield="name",
1606
                    qfilter=["=", "name", "othername"])
1607
    self.assertEqual(q.RequestedNames(), ["othername"])
1608
    self.assertEqual(q.Query(data), [])
1609

    
1610
    q = query.Query(fielddefs, ["name"], namefield="name",
1611
                    qfilter=["|", ["=", "name", "node1.example.com"],
1612
                                  ["=", "name", "node2"]])
1613
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1614
    self.assertEqual(q.Query(data), [
1615
      [(constants.RS_NORMAL, "node1.example.com")],
1616
      [(constants.RS_NORMAL, "node2.example.com")],
1617
      [(constants.RS_NORMAL, "node2.example.net")],
1618
      ])
1619
    self.assertEqual(q.OldStyleQuery(data), [
1620
      ["node1.example.com"],
1621
      ["node2.example.com"],
1622
      ["node2.example.net"],
1623
      ])
1624

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

    
1637
  def testFilterBoolean(self):
1638
    fielddefs = query._PrepareFieldList([
1639
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1640
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1641
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1642
       None, 0, lambda ctx, item: item["value"]),
1643
      ], [])
1644

    
1645
    data = [
1646
      { "name": "node1", "value": False, },
1647
      { "name": "node2", "value": True, },
1648
      { "name": "node3", "value": True, },
1649
      ]
1650

    
1651
    q = query.Query(fielddefs, ["name", "value"],
1652
                    qfilter=["|", ["=", "value", False],
1653
                                  ["=", "value", True]])
1654
    self.assertTrue(q.RequestedNames() is None)
1655
    self.assertEqual(q.Query(data), [
1656
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1657
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1658
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1659
      ])
1660

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

    
1671
    # Comparing bool with string
1672
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1673
      self.assertRaises(errors.ParameterError, query.Query,
1674
                        fielddefs, ["name", "value"],
1675
                        qfilter=["=", "value", i])
1676

    
1677
    # Truth filter
1678
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1679
    self.assertTrue(q.RequestedNames() is None)
1680
    self.assertEqual(q.Query(data), [
1681
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1682
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1683
      ])
1684

    
1685
    # Negative bool filter
1686
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1687
    self.assertTrue(q.RequestedNames() is None)
1688
    self.assertEqual(q.Query(data), [
1689
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1690
      ])
1691

    
1692
    # Complex truth filter
1693
    q = query.Query(fielddefs, ["name", "value"],
1694
                    qfilter=["|", ["&", ["=", "name", "node1"],
1695
                                        ["!", ["?", "value"]]],
1696
                                  ["?", "value"]])
1697
    self.assertTrue(q.RequestedNames() is None)
1698
    self.assertEqual(q.Query(data), [
1699
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1700
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1701
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1702
      ])
1703

    
1704
  def testFilterRegex(self):
1705
    fielddefs = query._PrepareFieldList([
1706
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1707
       None, 0, lambda ctx, item: item["name"]),
1708
      ], [])
1709

    
1710
    data = [
1711
      { "name": "node1.example.com", },
1712
      { "name": "node2.site.example.com", },
1713
      { "name": "node2.example.net", },
1714

    
1715
      # Empty name
1716
      { "name": "", },
1717
      ]
1718

    
1719
    q = query.Query(fielddefs, ["name"], namefield="name",
1720
                    qfilter=["=~", "name", "site"])
1721
    self.assertTrue(q.RequestedNames() is None)
1722
    self.assertEqual(q.Query(data), [
1723
      [(constants.RS_NORMAL, "node2.site.example.com")],
1724
      ])
1725

    
1726
    q = query.Query(fielddefs, ["name"], namefield="name",
1727
                    qfilter=["=~", "name", "^node2"])
1728
    self.assertTrue(q.RequestedNames() is None)
1729
    self.assertEqual(q.Query(data), [
1730
      [(constants.RS_NORMAL, "node2.example.net")],
1731
      [(constants.RS_NORMAL, "node2.site.example.com")],
1732
      ])
1733

    
1734
    q = query.Query(fielddefs, ["name"], namefield="name",
1735
                    qfilter=["=~", "name", r"(?i)\.COM$"])
1736
    self.assertTrue(q.RequestedNames() is None)
1737
    self.assertEqual(q.Query(data), [
1738
      [(constants.RS_NORMAL, "node1.example.com")],
1739
      [(constants.RS_NORMAL, "node2.site.example.com")],
1740
      ])
1741

    
1742
    q = query.Query(fielddefs, ["name"], namefield="name",
1743
                    qfilter=["=~", "name", r"."])
1744
    self.assertTrue(q.RequestedNames() is None)
1745
    self.assertEqual(q.Query(data), [
1746
      [(constants.RS_NORMAL, "node1.example.com")],
1747
      [(constants.RS_NORMAL, "node2.example.net")],
1748
      [(constants.RS_NORMAL, "node2.site.example.com")],
1749
      ])
1750

    
1751
    q = query.Query(fielddefs, ["name"], namefield="name",
1752
                    qfilter=["=~", "name", r"^$"])
1753
    self.assertTrue(q.RequestedNames() is None)
1754
    self.assertEqual(q.Query(data), [
1755
      [(constants.RS_NORMAL, "")],
1756
      ])
1757

    
1758
    # Invalid regular expression
1759
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1760
                      qfilter=["=~", "name", r"["])
1761

    
1762
  def testFilterLessGreater(self):
1763
    fielddefs = query._PrepareFieldList([
1764
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1765
       None, 0, lambda ctx, item: item),
1766
      ], [])
1767

    
1768
    data = range(100)
1769

    
1770
    q = query.Query(fielddefs, ["value"],
1771
                    qfilter=["<", "value", 20])
1772
    self.assertTrue(q.RequestedNames() is None)
1773
    self.assertEqual(q.Query(data),
1774
                     [[(constants.RS_NORMAL, i)] for i in range(20)])
1775

    
1776
    q = query.Query(fielddefs, ["value"],
1777
                    qfilter=["<=", "value", 30])
1778
    self.assertTrue(q.RequestedNames() is None)
1779
    self.assertEqual(q.Query(data),
1780
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1781

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

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

    
1794
  def testFilterLessGreaterJobId(self):
1795
    fielddefs = query._PrepareFieldList([
1796
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1797
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1798
      ], [])
1799

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

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

    
1804
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1805
    self.assertTrue(q.RequestedNames() is None)
1806
    self.assertEqual(q.Query(data), [
1807
      [(constants.RS_NORMAL, "1")],
1808
      [(constants.RS_NORMAL, "2")],
1809
      [(constants.RS_NORMAL, "3")],
1810
      [(constants.RS_NORMAL, "10")],
1811
      [(constants.RS_NORMAL, "15")],
1812
      [(constants.RS_NORMAL, "7")],
1813
      ])
1814

    
1815
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1816
    self.assertTrue(q.RequestedNames() is None)
1817
    self.assertEqual(q.Query(data), [
1818
      [(constants.RS_NORMAL, "102")],
1819
      [(constants.RS_NORMAL, "120")],
1820
      [(constants.RS_NORMAL, "125")],
1821
      [(constants.RS_NORMAL, "100")],
1822
      ])
1823

    
1824
    # Integers are no valid job IDs
1825
    self.assertRaises(errors.ParameterError, query.Query,
1826
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1827

    
1828
  def testFilterLessGreaterSplitTimestamp(self):
1829
    fielddefs = query._PrepareFieldList([
1830
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1831
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1832
      ], [])
1833

    
1834
    data = [
1835
      utils.SplitTime(0),
1836
      utils.SplitTime(0.1),
1837
      utils.SplitTime(18224.7872),
1838
      utils.SplitTime(919896.12623),
1839
      utils.SplitTime(999),
1840
      utils.SplitTime(989.9999),
1841
      ]
1842

    
1843
    for i in [0, [0, 0]]:
1844
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1845
      self.assertTrue(q.RequestedNames() is None)
1846
      self.assertEqual(q.Query(data), [])
1847

    
1848
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1849
    self.assertTrue(q.RequestedNames() is None)
1850
    self.assertEqual(q.Query(data), [
1851
      [(constants.RS_NORMAL, (0, 0))],
1852
      [(constants.RS_NORMAL, (0, 100000))],
1853
      [(constants.RS_NORMAL, (999, 0))],
1854
      [(constants.RS_NORMAL, (989, 999900))],
1855
      ])
1856

    
1857
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
1858
    self.assertTrue(q.RequestedNames() is None)
1859
    self.assertEqual(q.Query(data), [
1860
      [(constants.RS_NORMAL, (18224, 787200))],
1861
      [(constants.RS_NORMAL, (919896, 126230))],
1862
      ])
1863

    
1864
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1865
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1866
      self.assertTrue(q.RequestedNames() is None)
1867
      self.assertEqual(q.Query(data), [
1868
        [(constants.RS_NORMAL, (18224, 787200))],
1869
        [(constants.RS_NORMAL, (919896, 126230))],
1870
        ])
1871

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

    
1878

    
1879
if __name__ == "__main__":
1880
  testutils.GanetiTestProgram()