Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (73.5 kB)

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

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

    
21

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

    
24
import re
25
import unittest
26
import random
27

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

    
36
import ganeti.masterd.instance as gmi
37

    
38
import testutils
39

    
40

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

    
46

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

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

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

    
57

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

    
65

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
298

    
299
class TestGetNodeRole(unittest.TestCase):
300
  def test(self):
301
    tested_role = set()
302

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

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

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

    
321

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

    
326
  def testSimple(self):
327
    cluster = objects.Cluster(cluster_name="testcluster",
328
                              ndparams=constants.NDC_DEFAULTS.copy())
329
    grp1 = objects.NodeGroup(name="default",
330
                             uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
331
                             alloc_policy=constants.ALLOC_POLICY_PREFERRED,
332
                             ipolicy=objects.MakeEmptyIPolicy(),
333
                             ndparams={},
334
                             )
335
    grp2 = objects.NodeGroup(name="group2",
336
                             uuid="c0e89160-18e7-11e0-a46e-001d0904babe",
337
                             alloc_policy=constants.ALLOC_POLICY_PREFERRED,
338
                             ipolicy=objects.MakeEmptyIPolicy(),
339
                             ndparams={constants.ND_SPINDLE_COUNT: 2},
340
                             )
341
    groups = {grp1.uuid: grp1, grp2.uuid: grp2}
342
    nodes = [
343
      objects.Node(name="node1", drained=False, group=grp1.uuid, ndparams={}),
344
      objects.Node(name="node2", drained=True, group=grp2.uuid, ndparams={}),
345
      objects.Node(name="node3", drained=False, group=grp1.uuid,
346
                   ndparams={constants.ND_SPINDLE_COUNT: 4}),
347
      ]
348
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
349
      nqd = query.NodeQueryData(nodes, live_data, None, None, None,
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
                      utils.NiceSort(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, 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, query.IQ_NETWORKS]))
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
        disks_active=True,
681
        osparams={}),
682
      objects.Instance(name="inst2", hvparams={}, nics=[],
683
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
684
        ctime=1291211000, mtime=1291211077, serial_no=1,
685
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
686
        os="deb99",
687
        primary_node="node5",
688
        disk_template=constants.DT_DISKLESS,
689
        disks=[],
690
        disks_active=True,
691
        beparams={
692
          constants.BE_MAXMEM: 512,
693
          constants.BE_MINMEM: 256,
694
        },
695
        osparams={}),
696
      objects.Instance(name="inst3", hvparams={}, beparams={},
697
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
698
        ctime=1291011000, mtime=1291013000, serial_no=1923,
699
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_KVM,
700
        os="busybox",
701
        primary_node="node6",
702
        disk_template=constants.DT_DRBD8,
703
        disks=[],
704
        disks_active=False,
705
        nics=[
706
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
707
                      nicparams={
708
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
709
                        }),
710
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
711
          ],
712
        osparams={}),
713
      objects.Instance(name="inst4", hvparams={}, beparams={},
714
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
715
        ctime=1291244390, mtime=1291244395, serial_no=25,
716
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_PVM,
717
        os="linux1",
718
        primary_node="nodeoff2",
719
        disk_template=constants.DT_DRBD8,
720
        disks=[],
721
        disks_active=True,
722
        nics=[
723
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
724
                      nicparams={
725
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
726
                        }),
727
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
728
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
729
                      nicparams={
730
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
731
                        }),
732
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
733
                      nicparams={
734
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
735
                        constants.NIC_LINK: "eth123",
736
                        }),
737
          ],
738
        osparams={}),
739
      objects.Instance(name="inst5", hvparams={}, nics=[],
740
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
741
        ctime=1231211000, mtime=1261200000, serial_no=3,
742
        admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
743
        os="deb99",
744
        primary_node="nodebad2",
745
        disk_template=constants.DT_DISKLESS,
746
        disks=[],
747
        disks_active=True,
748
        beparams={
749
          constants.BE_MAXMEM: 512,
750
          constants.BE_MINMEM: 512,
751
        },
752
        osparams={}),
753
      objects.Instance(name="inst6", hvparams={}, nics=[],
754
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
755
        ctime=7513, mtime=11501, serial_no=13390,
756
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
757
        os="deb99",
758
        primary_node="node7",
759
        disk_template=constants.DT_DISKLESS,
760
        disks=[],
761
        disks_active=False,
762
        beparams={
763
          constants.BE_MAXMEM: 768,
764
          constants.BE_MINMEM: 256,
765
        },
766
        osparams={
767
          "clean_install": "no",
768
          }),
769
      objects.Instance(name="inst7", hvparams={}, nics=[],
770
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
771
        ctime=None, mtime=None, serial_no=1947,
772
        admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
773
        os="deb99",
774
        primary_node="node6",
775
        disk_template=constants.DT_DISKLESS,
776
        disks=[],
777
        disks_active=False,
778
        beparams={},
779
        osparams={}),
780
      objects.Instance(name="inst8", hvparams={}, nics=[],
781
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920f",
782
        ctime=None, mtime=None, serial_no=19478,
783
        admin_state=constants.ADMINST_OFFLINE, hypervisor=constants.HT_XEN_HVM,
784
        os="deb99",
785
        primary_node="node6",
786
        disk_template=constants.DT_DISKLESS,
787
        disks=[],
788
        disks_active=False,
789
        beparams={},
790
        osparams={}),
791
      ]
792

    
793
    assert not utils.FindDuplicates(inst.name for inst in instances)
794

    
795
    instbyname = dict((inst.name, inst) for inst in instances)
796

    
797
    disk_usage = dict((inst.name,
798
                       gmi.ComputeDiskSize(inst.disk_template,
799
                                           [{"size": disk.size}
800
                                           for disk in inst.disks]))
801
                      for inst in instances)
802

    
803
    inst_bridges = {
804
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
805
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
806
                None, "eth123"],
807
      }
808

    
809
    live_data = {
810
      "inst2": {
811
        "vcpus": 3,
812
        },
813
      "inst4": {
814
        "memory": 123,
815
        },
816
      "inst6": {
817
        "memory": 768,
818
        },
819
      "inst7": {
820
        "vcpus": 3,
821
        },
822
      }
823
    wrongnode_inst = set(["inst7"])
824

    
825
    consinfo = dict((inst.name, None) for inst in instances)
826
    consinfo["inst7"] = \
827
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
828
                              host=instbyname["inst7"].primary_node,
829
                              user="root",
830
                              command=["hostname"]).ToDict()
831

    
832
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
833
                                  offline_nodes, bad_nodes, live_data,
834
                                  wrongnode_inst, consinfo, {}, {}, {})
835
    result = q.Query(iqd)
836
    self.assertEqual(len(result), len(instances))
837
    self.assert_(compat.all(len(row) == len(selected)
838
                            for row in result))
839

    
840
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
841
           "Offline nodes not included in bad nodes"
842

    
843
    tested_status = set()
844

    
845
    for (inst, row) in zip(instances, result):
846
      assert inst.primary_node in nodes
847

    
848
      self.assertEqual(row[fieldidx["name"]],
849
                       (constants.RS_NORMAL, inst.name))
850

    
851
      if inst.primary_node in offline_nodes:
852
        exp_status = constants.INSTST_NODEOFFLINE
853
      elif inst.primary_node in bad_nodes:
854
        exp_status = constants.INSTST_NODEDOWN
855
      elif inst.name in live_data:
856
        if inst.name in wrongnode_inst:
857
          exp_status = constants.INSTST_WRONGNODE
858
        elif inst.admin_state == constants.ADMINST_UP:
859
          exp_status = constants.INSTST_RUNNING
860
        else:
861
          exp_status = constants.INSTST_ERRORUP
862
      elif inst.admin_state == constants.ADMINST_UP:
863
        exp_status = constants.INSTST_ERRORDOWN
864
      elif inst.admin_state == constants.ADMINST_DOWN:
865
        exp_status = constants.INSTST_ADMINDOWN
866
      else:
867
        exp_status = constants.INSTST_ADMINOFFLINE
868

    
869
      self.assertEqual(row[fieldidx["status"]],
870
                       (constants.RS_NORMAL, exp_status))
871

    
872
      (_, status) = row[fieldidx["status"]]
873
      tested_status.add(status)
874

    
875
      #FIXME(dynmem): check oper_ram vs min/max mem
876
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
877
        if inst.primary_node in bad_nodes:
878
          exp = (constants.RS_NODATA, None)
879
        elif inst.name in live_data:
880
          value = live_data[inst.name].get(livefield, None)
881
          if value is None:
882
            exp = (constants.RS_UNAVAIL, None)
883
          else:
884
            exp = (constants.RS_NORMAL, value)
885
        else:
886
          exp = (constants.RS_UNAVAIL, None)
887

    
888
        self.assertEqual(row[fieldidx[field]], exp)
889

    
890
      bridges = inst_bridges.get(inst.name, [])
891
      self.assertEqual(row[fieldidx["nic.bridges"]],
892
                       (constants.RS_NORMAL, bridges))
893
      if bridges:
894
        self.assertEqual(row[fieldidx["bridge"]],
895
                         (constants.RS_NORMAL, bridges[0]))
896
      else:
897
        self.assertEqual(row[fieldidx["bridge"]],
898
                         (constants.RS_UNAVAIL, None))
899

    
900
      for i in range(constants.MAX_NICS):
901
        if i < len(bridges) and bridges[i] is not None:
902
          exp = (constants.RS_NORMAL, bridges[i])
903
        else:
904
          exp = (constants.RS_UNAVAIL, None)
905
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
906

    
907
      if inst.primary_node in bad_nodes:
908
        exp = (constants.RS_NODATA, None)
909
      else:
910
        exp = (constants.RS_NORMAL, inst.name in live_data)
911
      self.assertEqual(row[fieldidx["oper_state"]], exp)
912

    
913
      cust_exp = (constants.RS_NORMAL, {})
914
      if inst.os == "deb99":
915
        if inst.name == "inst6":
916
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
917
          cust_exp = exp
918
        else:
919
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
920
      else:
921
        exp = (constants.RS_NORMAL, {})
922
      self.assertEqual(row[fieldidx["osparams"]], exp)
923
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
924

    
925
      usage = disk_usage[inst.name]
926
      if usage is None:
927
        usage = 0
928
      self.assertEqual(row[fieldidx["disk_usage"]],
929
                       (constants.RS_NORMAL, usage))
930

    
931
      for alias, target in [("sda_size", "disk.size/0"),
932
                            ("sdb_size", "disk.size/1"),
933
                            ("vcpus", "be/vcpus"),
934
                            ("ip", "nic.ip/0"),
935
                            ("mac", "nic.mac/0"),
936
                            ("bridge", "nic.bridge/0"),
937
                            ("nic_mode", "nic.mode/0"),
938
                            ("nic_link", "nic.link/0"),
939
                            ]:
940
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
941

    
942
      for field in ["ctime", "mtime"]:
943
        if getattr(inst, field) is None:
944
          # No ctime/mtime
945
          exp = (constants.RS_UNAVAIL, None)
946
        else:
947
          exp = (constants.RS_NORMAL, getattr(inst, field))
948
        self.assertEqual(row[fieldidx[field]], exp)
949

    
950
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
951

    
952
    # Ensure all possible status' have been tested
953
    self.assertEqual(tested_status, constants.INSTST_ALL)
954

    
955
  def _CheckInstanceConsole(self, instance, (status, consdata)):
956
    if instance.name == "inst7":
957
      self.assertEqual(status, constants.RS_NORMAL)
958
      console = objects.InstanceConsole.FromDict(consdata)
959
      self.assertTrue(console.Validate())
960
      self.assertEqual(console.host, instance.primary_node)
961
    else:
962
      self.assertEqual(status, constants.RS_UNAVAIL)
963

    
964

    
965
class TestGroupQuery(unittest.TestCase):
966

    
967
  def setUp(self):
968
    self.custom_diskparams = {
969
      constants.DT_DRBD8: {
970
        constants.DRBD_DEFAULT_METAVG: "foobar",
971
      },
972
    }
973

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

    
1003
  def _Create(self, selected):
1004
    return query.Query(query.GROUP_FIELDS, selected)
1005

    
1006
  def testSimple(self):
1007
    q = self._Create(["name", "uuid", "alloc_policy"])
1008
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1009

    
1010
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1011

    
1012
    self.assertEqual(q.Query(gqd),
1013
      [[(constants.RS_NORMAL, "default"),
1014
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1015
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1016
        ],
1017
       [(constants.RS_NORMAL, "restricted"),
1018
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1019
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1020
        ],
1021
       ])
1022

    
1023
  def testNodes(self):
1024
    groups_to_nodes = {
1025
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1026
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1027
      }
1028

    
1029
    q = self._Create(["name", "node_cnt", "node_list"])
1030
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1031
                               False)
1032

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

    
1035
    self.assertEqual(q.Query(gqd),
1036
                     [[(constants.RS_NORMAL, "default"),
1037
                       (constants.RS_NORMAL, 2),
1038
                       (constants.RS_NORMAL, ["node1", "node2"]),
1039
                       ],
1040
                      [(constants.RS_NORMAL, "restricted"),
1041
                       (constants.RS_NORMAL, 3),
1042
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1043
                       ],
1044
                      ])
1045

    
1046
  def testInstances(self):
1047
    groups_to_instances = {
1048
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1049
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1050
      }
1051

    
1052
    q = self._Create(["pinst_cnt", "pinst_list"])
1053
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1054
      groups_to_instances, False)
1055

    
1056
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1057

    
1058
    self.assertEqual(q.Query(gqd),
1059
                     [[(constants.RS_NORMAL, 2),
1060
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1061
                       ],
1062
                      [(constants.RS_NORMAL, 3),
1063
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1064
                       ],
1065
                      ])
1066

    
1067
  def testDiskparams(self):
1068
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1069
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1070

    
1071
    self.assertEqual(q.RequestedData(),
1072
                     set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1073

    
1074
    self.assertEqual(q.Query(gqd),
1075
      [[(constants.RS_NORMAL, "default"),
1076
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1077
        (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1078
        (constants.RS_NORMAL, {}),
1079
        ],
1080
       [(constants.RS_NORMAL, "restricted"),
1081
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1082
        (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1083
                                                     self.custom_diskparams)),
1084
        (constants.RS_NORMAL, self.custom_diskparams),
1085
        ],
1086
       ])
1087

    
1088

    
1089
class TestOsQuery(unittest.TestCase):
1090
  def _Create(self, selected):
1091
    return query.Query(query.OS_FIELDS, selected)
1092

    
1093
  def test(self):
1094
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1095
    api_versions = [10, 0, 15, 5]
1096
    parameters = ["zpar3", "apar9"]
1097

    
1098
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1099
    assert (api_versions != sorted(api_versions) and
1100
            api_versions != utils.NiceSort(variants))
1101
    assert (parameters != sorted(parameters) and
1102
            parameters != utils.NiceSort(parameters))
1103

    
1104
    data = [
1105
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1106
                   variants=set(), api_versions=set(), parameters=set(),
1107
                   node_status={ "some": "status", }),
1108
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1109
                   variants=set(variants),
1110
                   api_versions=set(api_versions),
1111
                   parameters=set(parameters),
1112
                   node_status={ "some": "other", "status": None, }),
1113
      ]
1114

    
1115

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

    
1140

    
1141
class TestQueryFields(unittest.TestCase):
1142
  def testAllFields(self):
1143
    for fielddefs in query.ALL_FIELD_LISTS:
1144
      result = query.QueryFields(fielddefs, None)
1145
      self.assert_(isinstance(result, dict))
1146
      response = objects.QueryFieldsResponse.FromDict(result)
1147
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1148
        [(fdef2.name, fdef2.title)
1149
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1150
                                                key=lambda x: x[0].name)])
1151

    
1152
  def testSomeFields(self):
1153
    rnd = random.Random(5357)
1154

    
1155
    for _ in range(10):
1156
      for fielddefs in query.ALL_FIELD_LISTS:
1157
        if len(fielddefs) > 20:
1158
          sample_size = rnd.randint(5, 20)
1159
        else:
1160
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1161
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1162
                                                         sample_size)]
1163
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1164
        self.assert_(isinstance(result, dict))
1165
        response = objects.QueryFieldsResponse.FromDict(result)
1166
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1167
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1168

    
1169

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

    
1189
      assert namefield in fielddefs
1190

    
1191
      reqnames = [genval(i) for i in range(4)]
1192
      innerfilter = [["=", namefield, v] for v in reqnames]
1193

    
1194
      # No name field
1195
      q = query.Query(fielddefs, [namefield],
1196
                      qfilter=["=", namefield, nameval], namefield=None)
1197
      self.assertEqual(q.RequestedNames(), None)
1198

    
1199
      # No filter
1200
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1201
      self.assertEqual(q.RequestedNames(), None)
1202

    
1203
      # Check empty query
1204
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1205
                      namefield=namefield)
1206
      self.assertEqual(q.RequestedNames(), None)
1207

    
1208
      # Check order
1209
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1210
                      namefield=namefield)
1211
      self.assertEqual(q.RequestedNames(), reqnames)
1212

    
1213
      # Check reverse order
1214
      q = query.Query(fielddefs, [namefield],
1215
                      qfilter=["|"] + list(reversed(innerfilter)),
1216
                      namefield=namefield)
1217
      self.assertEqual(q.RequestedNames(), list(reversed(reqnames)))
1218

    
1219
      # Duplicates
1220
      q = query.Query(fielddefs, [namefield],
1221
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1222
                      namefield=namefield)
1223
      self.assertEqual(q.RequestedNames(), reqnames)
1224

    
1225
      # Unknown name field
1226
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1227
                        namefield="_unknown_field_")
1228

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

    
1236
      # Filter with NOT
1237
      q = query.Query(fielddefs, [namefield],
1238
                      qfilter=["|", ["=", namefield, nameval],
1239
                                    ["!", ["=", namefield, namevalempty]]],
1240
                      namefield=namefield)
1241
      self.assertTrue(q.RequestedNames() is None)
1242

    
1243
      # Filter with only OR (names must be in correct order)
1244
      q = query.Query(fielddefs, [namefield],
1245
                      qfilter=["|", ["=", namefield, randvals[0]],
1246
                                    ["|", ["=", namefield, randvals[1]]],
1247
                                    ["|", ["|", ["=", namefield, randvals[2]]]],
1248
                                    ["=", namefield, randvals[3]]],
1249
                      namefield=namefield)
1250
      self.assertEqual(q.RequestedNames(), randvals)
1251

    
1252
  @staticmethod
1253
  def _GenNestedFilter(namefield, op, depth, nameval):
1254
    nested = ["=", namefield, nameval]
1255
    for i in range(depth):
1256
      nested = [op, nested]
1257
    return nested
1258

    
1259
  def testCompileFilter(self):
1260
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1261

    
1262
    for (what, fielddefs) in query.ALL_FIELDS.items():
1263
      if what == constants.QR_JOB:
1264
        namefield = "id"
1265
        nameval = 123
1266
      elif what == constants.QR_EXPORT:
1267
        namefield = "export"
1268
        nameval = "value"
1269
      else:
1270
        namefield = "name"
1271
        nameval = "value"
1272

    
1273
      checks = [
1274
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1275
        ["=", "_unknown_field", "value"],
1276
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1277
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1278
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1279
        ]
1280

    
1281
      for qfilter in checks:
1282
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1283
                          fielddefs, None, qfilter)
1284

    
1285
      for op in ["|", "!"]:
1286
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1287
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1288
                                                      qfilter)))
1289

    
1290
  def testQueryInputOrder(self):
1291
    fielddefs = query._PrepareFieldList([
1292
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1293
       None, 0, lambda ctx, item: item["pnode"]),
1294
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1295
       None, 0, lambda ctx, item: item["snode"]),
1296
      ], [])
1297

    
1298
    data = [
1299
      { "pnode": "node1", "snode": "node44", },
1300
      { "pnode": "node30", "snode": "node90", },
1301
      { "pnode": "node25", "snode": "node1", },
1302
      { "pnode": "node20", "snode": "node1", },
1303
      ]
1304

    
1305
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1306

    
1307
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1308
                    qfilter=qfilter)
1309
    self.assertTrue(q.RequestedNames() is None)
1310
    self.assertFalse(q.RequestedData())
1311
    self.assertEqual(q.Query(data),
1312
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1313
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1314
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1315

    
1316
    # Try again with reversed input data
1317
    self.assertEqual(q.Query(reversed(data)),
1318
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1319
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1320
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1321

    
1322
    # No name field, result must be in incoming order
1323
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1324
                    qfilter=qfilter)
1325
    self.assertFalse(q.RequestedData())
1326
    self.assertEqual(q.Query(data),
1327
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1328
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1329
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1330
    self.assertEqual(q.OldStyleQuery(data), [
1331
      ["node1", "node44"],
1332
      ["node25", "node1"],
1333
      ["node20", "node1"],
1334
      ])
1335
    self.assertEqual(q.Query(reversed(data)),
1336
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1337
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1338
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1339
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1340
      ["node20", "node1"],
1341
      ["node25", "node1"],
1342
      ["node1", "node44"],
1343
      ])
1344

    
1345
    # Name field, but no sorting, result must be in incoming order
1346
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1347
    self.assertFalse(q.RequestedData())
1348
    self.assertEqual(q.Query(data, sort_by_name=False),
1349
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1350
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1351
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1352
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1353
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1354
      ["node1", "node44"],
1355
      ["node30", "node90"],
1356
      ["node25", "node1"],
1357
      ["node20", "node1"],
1358
      ])
1359
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1360
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1361
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1362
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1363
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1364
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1365
      ["node20", "node1"],
1366
      ["node25", "node1"],
1367
      ["node30", "node90"],
1368
      ["node1", "node44"],
1369
      ])
1370

    
1371
  def testEqualNamesOrder(self):
1372
    fielddefs = query._PrepareFieldList([
1373
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1374
       None, 0, lambda ctx, item: item["pnode"]),
1375
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1376
       None, 0, lambda ctx, item: item["num"]),
1377
      ], [])
1378

    
1379
    data = [
1380
      { "pnode": "node1", "num": 100, },
1381
      { "pnode": "node1", "num": 25, },
1382
      { "pnode": "node2", "num": 90, },
1383
      { "pnode": "node2", "num": 30, },
1384
      ]
1385

    
1386
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1387
                    qfilter=["|", ["=", "pnode", "node1"],
1388
                                  ["=", "pnode", "node2"],
1389
                                  ["=", "pnode", "node1"]])
1390
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1391
                     msg="Did not return unique names")
1392
    self.assertFalse(q.RequestedData())
1393
    self.assertEqual(q.Query(data),
1394
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1395
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1396
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1397
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1398
    self.assertEqual(q.Query(data, sort_by_name=False),
1399
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1400
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1401
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1402
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1403

    
1404
    data = [
1405
      { "pnode": "nodeX", "num": 50, },
1406
      { "pnode": "nodeY", "num": 40, },
1407
      { "pnode": "nodeX", "num": 30, },
1408
      { "pnode": "nodeX", "num": 20, },
1409
      { "pnode": "nodeM", "num": 10, },
1410
      ]
1411

    
1412
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1413
                    qfilter=["|", ["=", "pnode", "nodeX"],
1414
                                  ["=", "pnode", "nodeY"],
1415
                                  ["=", "pnode", "nodeY"],
1416
                                  ["=", "pnode", "nodeY"],
1417
                                  ["=", "pnode", "nodeM"]])
1418
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1419
                     msg="Did not return unique names")
1420
    self.assertFalse(q.RequestedData())
1421

    
1422
    # First sorted by name, then input order
1423
    self.assertEqual(q.Query(data, sort_by_name=True),
1424
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1425
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1426
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1427
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1428
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1429

    
1430
    # Input order
1431
    self.assertEqual(q.Query(data, sort_by_name=False),
1432
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1433
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1434
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1435
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1436
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1437

    
1438
  def testFilter(self):
1439
    (DK_A, DK_B) = range(1000, 1002)
1440

    
1441
    fielddefs = query._PrepareFieldList([
1442
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1443
       DK_A, 0, lambda ctx, item: item["name"]),
1444
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1445
       DK_B, 0, lambda ctx, item: item["other"]),
1446
      ], [])
1447

    
1448
    data = [
1449
      { "name": "node1", "other": "foo", },
1450
      { "name": "node2", "other": "bar", },
1451
      { "name": "node3", "other": "Hello", },
1452
      ]
1453

    
1454
    # Empty filter
1455
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1456
                    qfilter=["|"])
1457
    self.assertTrue(q.RequestedNames() is None)
1458
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1459
    self.assertEqual(q.Query(data), [])
1460

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

    
1468
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1469
                    qfilter=(["|", ["=", "name", "node1"],
1470
                                   ["=", "name", "node3"]]))
1471
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1472
    self.assertEqual(q.Query(data),
1473
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1474
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1475

    
1476
    # Complex filter
1477
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1478
                    qfilter=(["|", ["=", "name", "node1"],
1479
                                   ["|", ["=", "name", "node3"],
1480
                                         ["=", "name", "node2"]],
1481
                                   ["=", "name", "node3"]]))
1482
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1483
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1484
    self.assertEqual(q.Query(data),
1485
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1486
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1487
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1488

    
1489
    # Filter data type mismatch
1490
    for i in [-1, 0, 1, 123, [], None, True, False]:
1491
      self.assertRaises(errors.ParameterError, query.Query,
1492
                        fielddefs, ["name", "other"], namefield="name",
1493
                        qfilter=["=", "name", i])
1494

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

    
1503
    # Not equal
1504
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1505
                    qfilter=["!=", "name", "node3"])
1506
    self.assertTrue(q.RequestedNames() is None)
1507
    self.assertEqual(q.Query(data),
1508
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1509
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1510

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

    
1519
    # Only one data type
1520
    q = query.Query(fielddefs, ["other"], namefield="name",
1521
                    qfilter=["=", "other", "bar"])
1522
    self.assertTrue(q.RequestedNames() is None)
1523
    self.assertEqual(q.RequestedData(), set([DK_B]))
1524
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1525

    
1526
    q = query.Query(fielddefs, [], namefield="name",
1527
                    qfilter=["=", "other", "bar"])
1528
    self.assertTrue(q.RequestedNames() is None)
1529
    self.assertEqual(q.RequestedData(), set([DK_B]))
1530
    self.assertEqual(q.Query(data), [[]])
1531

    
1532
    # Data type in boolean operator
1533
    q = query.Query(fielddefs, [], namefield="name",
1534
                    qfilter=["?", "name"])
1535
    self.assertTrue(q.RequestedNames() is None)
1536
    self.assertEqual(q.RequestedData(), set([DK_A]))
1537
    self.assertEqual(q.Query(data), [[], [], []])
1538

    
1539
    q = query.Query(fielddefs, [], namefield="name",
1540
                    qfilter=["!", ["?", "name"]])
1541
    self.assertTrue(q.RequestedNames() is None)
1542
    self.assertEqual(q.RequestedData(), set([DK_A]))
1543
    self.assertEqual(q.Query(data), [])
1544

    
1545
  def testFilterContains(self):
1546
    fielddefs = query._PrepareFieldList([
1547
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1548
       None, 0, lambda ctx, item: item["name"]),
1549
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1550
       None, 0, lambda ctx, item: item["other"]),
1551
      ], [])
1552

    
1553
    data = [
1554
      { "name": "node2", "other": ["x", "y", "bar"], },
1555
      { "name": "node3", "other": "Hello", },
1556
      { "name": "node1", "other": ["a", "b", "foo"], },
1557
      { "name": "empty", "other": []},
1558
      ]
1559

    
1560
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1561
                    qfilter=["=[]", "other", "bar"])
1562
    self.assertTrue(q.RequestedNames() is None)
1563
    self.assertEqual(q.Query(data), [
1564
      [(constants.RS_NORMAL, "node2"),
1565
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1566
      ])
1567

    
1568
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1569
                    qfilter=["|", ["=[]", "other", "bar"],
1570
                                  ["=[]", "other", "a"],
1571
                                  ["=[]", "other", "b"]])
1572
    self.assertTrue(q.RequestedNames() is None)
1573
    self.assertEqual(q.Query(data), [
1574
      [(constants.RS_NORMAL, "node1"),
1575
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1576
      [(constants.RS_NORMAL, "node2"),
1577
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1578
      ])
1579
    self.assertEqual(q.OldStyleQuery(data), [
1580
      ["node1", ["a", "b", "foo"]],
1581
      ["node2", ["x", "y", "bar"]],
1582
      ])
1583

    
1584
    # Boolean test
1585
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1586
                    qfilter=["?", "other"])
1587
    self.assertEqual(q.OldStyleQuery(data), [
1588
      ["node1", ["a", "b", "foo"]],
1589
      ["node2", ["x", "y", "bar"]],
1590
      ["node3", "Hello"],
1591
      ])
1592

    
1593
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1594
                    qfilter=["!", ["?", "other"]])
1595
    self.assertEqual(q.OldStyleQuery(data), [
1596
      ["empty", []],
1597
      ])
1598

    
1599
  def testFilterHostname(self):
1600
    fielddefs = query._PrepareFieldList([
1601
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1602
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1603
      ], [])
1604

    
1605
    data = [
1606
      { "name": "node1.example.com", },
1607
      { "name": "node2.example.com", },
1608
      { "name": "node2.example.net", },
1609
      ]
1610

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

    
1619
    q = query.Query(fielddefs, ["name"], namefield="name",
1620
                    qfilter=["=", "name", "node1"])
1621
    self.assertEqual(q.RequestedNames(), ["node1"])
1622
    self.assertEqual(q.Query(data), [
1623
      [(constants.RS_NORMAL, "node1.example.com")],
1624
      ])
1625

    
1626
    q = query.Query(fielddefs, ["name"], namefield="name",
1627
                    qfilter=["=", "name", "othername"])
1628
    self.assertEqual(q.RequestedNames(), ["othername"])
1629
    self.assertEqual(q.Query(data), [])
1630

    
1631
    q = query.Query(fielddefs, ["name"], namefield="name",
1632
                    qfilter=["|", ["=", "name", "node1.example.com"],
1633
                                  ["=", "name", "node2"]])
1634
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1635
    self.assertEqual(q.Query(data), [
1636
      [(constants.RS_NORMAL, "node1.example.com")],
1637
      [(constants.RS_NORMAL, "node2.example.com")],
1638
      [(constants.RS_NORMAL, "node2.example.net")],
1639
      ])
1640
    self.assertEqual(q.OldStyleQuery(data), [
1641
      ["node1.example.com"],
1642
      ["node2.example.com"],
1643
      ["node2.example.net"],
1644
      ])
1645

    
1646
    q = query.Query(fielddefs, ["name"], namefield="name",
1647
                    qfilter=["!=", "name", "node1"])
1648
    self.assertTrue(q.RequestedNames() is None)
1649
    self.assertEqual(q.Query(data), [
1650
      [(constants.RS_NORMAL, "node2.example.com")],
1651
      [(constants.RS_NORMAL, "node2.example.net")],
1652
      ])
1653
    self.assertEqual(q.OldStyleQuery(data), [
1654
      ["node2.example.com"],
1655
      ["node2.example.net"],
1656
      ])
1657

    
1658
  def testFilterBoolean(self):
1659
    fielddefs = query._PrepareFieldList([
1660
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1661
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1662
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1663
       None, 0, lambda ctx, item: item["value"]),
1664
      ], [])
1665

    
1666
    data = [
1667
      { "name": "node1", "value": False, },
1668
      { "name": "node2", "value": True, },
1669
      { "name": "node3", "value": True, },
1670
      ]
1671

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

    
1682
    q = query.Query(fielddefs, ["name", "value"],
1683
                    qfilter=["|", ["=", "value", False],
1684
                                  ["!", ["=", "value", False]]])
1685
    self.assertTrue(q.RequestedNames() is None)
1686
    self.assertEqual(q.Query(data), [
1687
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1688
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1689
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1690
      ])
1691

    
1692
    # Comparing bool with string
1693
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1694
      self.assertRaises(errors.ParameterError, query.Query,
1695
                        fielddefs, ["name", "value"],
1696
                        qfilter=["=", "value", i])
1697

    
1698
    # Truth 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, "node2"), (constants.RS_NORMAL, True)],
1703
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1704
      ])
1705

    
1706
    # Negative bool filter
1707
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1708
    self.assertTrue(q.RequestedNames() is None)
1709
    self.assertEqual(q.Query(data), [
1710
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1711
      ])
1712

    
1713
    # Complex truth filter
1714
    q = query.Query(fielddefs, ["name", "value"],
1715
                    qfilter=["|", ["&", ["=", "name", "node1"],
1716
                                        ["!", ["?", "value"]]],
1717
                                  ["?", "value"]])
1718
    self.assertTrue(q.RequestedNames() is None)
1719
    self.assertEqual(q.Query(data), [
1720
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1721
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1722
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1723
      ])
1724

    
1725
  def testFilterRegex(self):
1726
    fielddefs = query._PrepareFieldList([
1727
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1728
       None, 0, lambda ctx, item: item["name"]),
1729
      ], [])
1730

    
1731
    data = [
1732
      { "name": "node1.example.com", },
1733
      { "name": "node2.site.example.com", },
1734
      { "name": "node2.example.net", },
1735

    
1736
      # Empty name
1737
      { "name": "", },
1738
      ]
1739

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

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

    
1755
    q = query.Query(fielddefs, ["name"], namefield="name",
1756
                    qfilter=["=~", "name", r"(?i)\.COM$"])
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.site.example.com")],
1761
      ])
1762

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

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

    
1779
    # Invalid regular expression
1780
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1781
                      qfilter=["=~", "name", r"["])
1782

    
1783
  def testFilterLessGreater(self):
1784
    fielddefs = query._PrepareFieldList([
1785
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1786
       None, 0, lambda ctx, item: item),
1787
      ], [])
1788

    
1789
    data = range(100)
1790

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

    
1797
    q = query.Query(fielddefs, ["value"],
1798
                    qfilter=["<=", "value", 30])
1799
    self.assertTrue(q.RequestedNames() is None)
1800
    self.assertEqual(q.Query(data),
1801
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1802

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

    
1809
    q = query.Query(fielddefs, ["value"],
1810
                    qfilter=[">=", "value", 50])
1811
    self.assertTrue(q.RequestedNames() is None)
1812
    self.assertEqual(q.Query(data),
1813
                     [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
1814

    
1815
  def testFilterLessGreaterJobId(self):
1816
    fielddefs = query._PrepareFieldList([
1817
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1818
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1819
      ], [])
1820

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

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

    
1825
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1826
    self.assertTrue(q.RequestedNames() is None)
1827
    self.assertEqual(q.Query(data), [
1828
      [(constants.RS_NORMAL, "1")],
1829
      [(constants.RS_NORMAL, "2")],
1830
      [(constants.RS_NORMAL, "3")],
1831
      [(constants.RS_NORMAL, "10")],
1832
      [(constants.RS_NORMAL, "15")],
1833
      [(constants.RS_NORMAL, "7")],
1834
      ])
1835

    
1836
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1837
    self.assertTrue(q.RequestedNames() is None)
1838
    self.assertEqual(q.Query(data), [
1839
      [(constants.RS_NORMAL, "102")],
1840
      [(constants.RS_NORMAL, "120")],
1841
      [(constants.RS_NORMAL, "125")],
1842
      [(constants.RS_NORMAL, "100")],
1843
      ])
1844

    
1845
    # Integers are no valid job IDs
1846
    self.assertRaises(errors.ParameterError, query.Query,
1847
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1848

    
1849
  def testFilterLessGreaterSplitTimestamp(self):
1850
    fielddefs = query._PrepareFieldList([
1851
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1852
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1853
      ], [])
1854

    
1855
    data = [
1856
      utils.SplitTime(0),
1857
      utils.SplitTime(0.1),
1858
      utils.SplitTime(18224.7872),
1859
      utils.SplitTime(919896.12623),
1860
      utils.SplitTime(999),
1861
      utils.SplitTime(989.9999),
1862
      ]
1863

    
1864
    for i in [0, [0, 0]]:
1865
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1866
      self.assertTrue(q.RequestedNames() is None)
1867
      self.assertEqual(q.Query(data), [])
1868

    
1869
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1870
    self.assertTrue(q.RequestedNames() is None)
1871
    self.assertEqual(q.Query(data), [
1872
      [(constants.RS_NORMAL, (0, 0))],
1873
      [(constants.RS_NORMAL, (0, 100000))],
1874
      [(constants.RS_NORMAL, (999, 0))],
1875
      [(constants.RS_NORMAL, (989, 999900))],
1876
      ])
1877

    
1878
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
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
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1886
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1887
      self.assertTrue(q.RequestedNames() is None)
1888
      self.assertEqual(q.Query(data), [
1889
        [(constants.RS_NORMAL, (18224, 787200))],
1890
        [(constants.RS_NORMAL, (919896, 126230))],
1891
        ])
1892

    
1893
    q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880])
1894
    self.assertTrue(q.RequestedNames() is None)
1895
    self.assertEqual(q.Query(data), [
1896
      [(constants.RS_NORMAL, (919896, 126230))],
1897
      ])
1898

    
1899

    
1900
if __name__ == "__main__":
1901
  testutils.GanetiTestProgram()