Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (74 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_node = nodes[4]
427
    assert live_data_node.name != master_name
428

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

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

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

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

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

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

    
462
    oob_not_powered_node = nodes[0]
463
    oob_not_powered_node.powered = False
464
    oob_support = dict((node.uuid, False) for node in nodes)
465
    oob_support[master_node.uuid] = True
466
    oob_support[oob_not_powered_node.uuid] = True
467

    
468
    master_node.group = ng_uuid
469

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

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

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

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

    
514
    live_data_row = result[node_to_row[live_data_node.name]]
515

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

    
520
    self.assertEqual(master_row[field_index["pinst_cnt"]],
521
                     (constants.RS_NORMAL, 2))
522
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
523
                     (constants.RS_NORMAL, 3))
524
    self.assertEqual(master_row[field_index["pinst_list"]],
525
                     (constants.RS_NORMAL,
526
                      list(node_to_primary[master_node.uuid])))
527
    self.assertEqual(live_data_row[field_index["sinst_list"]],
528
                     (constants.RS_NORMAL,
529
                      utils.NiceSort(list(
530
                        node_to_secondary[live_data_node.uuid]))))
531

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

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

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

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

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

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

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

    
590

    
591
class TestInstanceQuery(unittest.TestCase):
592
  def _Create(self, selected):
593
    return query.Query(query.INSTANCE_FIELDS, selected)
594

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

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

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

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

    
642
  def test(self):
643
    selected = query.INSTANCE_FIELDS.keys()
644
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
645

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

    
649
    q = self._Create(selected)
650
    self.assertEqual(q.RequestedData(),
651
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
652
                          query.IQ_CONSOLE, query.IQ_NODES, query.IQ_NETWORKS]))
653

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

    
670
    offline_nodes = ["nodeoff1-uuid", "nodeoff2-uuid"]
671
    bad_nodes = ["nodebad1-uuid", "nodebad2-uuid", "nodebad3-uuid"] +\
672
                offline_nodes
673
    node_uuids = ["node%s-uuid" % i for i in range(10)] + bad_nodes
674

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

    
797
    assert not utils.FindDuplicates(inst.name for inst in instances)
798

    
799
    instbyname = dict((inst.name, inst) for inst in instances)
800

    
801
    disk_usage = dict((inst.name,
802
                       gmi.ComputeDiskSize(inst.disk_template,
803
                                           [{"size": disk.size}
804
                                           for disk in inst.disks]))
805
                      for inst in instances)
806

    
807
    inst_bridges = {
808
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
809
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
810
                None, "eth123"],
811
      }
812

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

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

    
836
    nodes = dict([(uuid, objects.Node(
837
                           name="%s.example.com" % uuid,
838
                           uuid=uuid,
839
                           group="default-uuid"))
840
                  for uuid in node_uuids])
841

    
842
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
843
                                  offline_nodes, bad_nodes, live_data,
844
                                  wrongnode_inst, consinfo, nodes, {}, {})
845
    result = q.Query(iqd)
846
    self.assertEqual(len(result), len(instances))
847
    self.assert_(compat.all(len(row) == len(selected)
848
                            for row in result))
849

    
850
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
851
           "Offline nodes not included in bad nodes"
852

    
853
    tested_status = set()
854

    
855
    for (inst, row) in zip(instances, result):
856
      assert inst.primary_node in node_uuids
857

    
858
      self.assertEqual(row[fieldidx["name"]],
859
                       (constants.RS_NORMAL, inst.name))
860

    
861
      if inst.primary_node in offline_nodes:
862
        exp_status = constants.INSTST_NODEOFFLINE
863
      elif inst.primary_node in bad_nodes:
864
        exp_status = constants.INSTST_NODEDOWN
865
      elif inst.name in live_data:
866
        if inst.name in wrongnode_inst:
867
          exp_status = constants.INSTST_WRONGNODE
868
        elif inst.admin_state == constants.ADMINST_UP:
869
          exp_status = constants.INSTST_RUNNING
870
        else:
871
          exp_status = constants.INSTST_ERRORUP
872
      elif inst.admin_state == constants.ADMINST_UP:
873
        exp_status = constants.INSTST_ERRORDOWN
874
      elif inst.admin_state == constants.ADMINST_DOWN:
875
        exp_status = constants.INSTST_ADMINDOWN
876
      else:
877
        exp_status = constants.INSTST_ADMINOFFLINE
878

    
879
      self.assertEqual(row[fieldidx["status"]],
880
                       (constants.RS_NORMAL, exp_status))
881

    
882
      (_, status) = row[fieldidx["status"]]
883
      tested_status.add(status)
884

    
885
      #FIXME(dynmem): check oper_ram vs min/max mem
886
      for (field, livefield) in [("oper_vcpus", "vcpus")]:
887
        if inst.primary_node in bad_nodes:
888
          exp = (constants.RS_NODATA, None)
889
        elif inst.name in live_data:
890
          value = live_data[inst.name].get(livefield, None)
891
          if value is None:
892
            exp = (constants.RS_UNAVAIL, None)
893
          else:
894
            exp = (constants.RS_NORMAL, value)
895
        else:
896
          exp = (constants.RS_UNAVAIL, None)
897

    
898
        self.assertEqual(row[fieldidx[field]], exp)
899

    
900
      bridges = inst_bridges.get(inst.name, [])
901
      self.assertEqual(row[fieldidx["nic.bridges"]],
902
                       (constants.RS_NORMAL, bridges))
903
      if bridges:
904
        self.assertEqual(row[fieldidx["bridge"]],
905
                         (constants.RS_NORMAL, bridges[0]))
906
      else:
907
        self.assertEqual(row[fieldidx["bridge"]],
908
                         (constants.RS_UNAVAIL, None))
909

    
910
      for i in range(constants.MAX_NICS):
911
        if i < len(bridges) and bridges[i] is not None:
912
          exp = (constants.RS_NORMAL, bridges[i])
913
        else:
914
          exp = (constants.RS_UNAVAIL, None)
915
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
916

    
917
      if inst.primary_node in bad_nodes:
918
        exp = (constants.RS_NODATA, None)
919
      else:
920
        exp = (constants.RS_NORMAL, inst.name in live_data)
921
      self.assertEqual(row[fieldidx["oper_state"]], exp)
922

    
923
      cust_exp = (constants.RS_NORMAL, {})
924
      if inst.os == "deb99":
925
        if inst.name == "inst6":
926
          exp = (constants.RS_NORMAL, {"clean_install": "no"})
927
          cust_exp = exp
928
        else:
929
          exp = (constants.RS_NORMAL, {"clean_install": "yes"})
930
      else:
931
        exp = (constants.RS_NORMAL, {})
932
      self.assertEqual(row[fieldidx["osparams"]], exp)
933
      self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
934

    
935
      usage = disk_usage[inst.name]
936
      if usage is None:
937
        usage = 0
938
      self.assertEqual(row[fieldidx["disk_usage"]],
939
                       (constants.RS_NORMAL, usage))
940

    
941
      for alias, target in [("sda_size", "disk.size/0"),
942
                            ("sdb_size", "disk.size/1"),
943
                            ("vcpus", "be/vcpus"),
944
                            ("ip", "nic.ip/0"),
945
                            ("mac", "nic.mac/0"),
946
                            ("bridge", "nic.bridge/0"),
947
                            ("nic_mode", "nic.mode/0"),
948
                            ("nic_link", "nic.link/0"),
949
                            ]:
950
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
951

    
952
      for field in ["ctime", "mtime"]:
953
        if getattr(inst, field) is None:
954
          # No ctime/mtime
955
          exp = (constants.RS_UNAVAIL, None)
956
        else:
957
          exp = (constants.RS_NORMAL, getattr(inst, field))
958
        self.assertEqual(row[fieldidx[field]], exp)
959

    
960
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
961

    
962
    # Ensure all possible status' have been tested
963
    self.assertEqual(tested_status, constants.INSTST_ALL)
964

    
965
  def _CheckInstanceConsole(self, instance, (status, consdata)):
966
    if instance.name == "inst7":
967
      self.assertEqual(status, constants.RS_NORMAL)
968
      console = objects.InstanceConsole.FromDict(consdata)
969
      self.assertTrue(console.Validate())
970
      self.assertEqual(console.host, instance.primary_node)
971
    else:
972
      self.assertEqual(status, constants.RS_UNAVAIL)
973

    
974

    
975
class TestGroupQuery(unittest.TestCase):
976

    
977
  def setUp(self):
978
    self.custom_diskparams = {
979
      constants.DT_DRBD8: {
980
        constants.DRBD_DEFAULT_METAVG: "foobar",
981
      },
982
    }
983

    
984
    self.groups = [
985
      objects.NodeGroup(name="default",
986
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
987
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED,
988
                        ipolicy=objects.MakeEmptyIPolicy(),
989
                        ndparams={},
990
                        diskparams={},
991
                        ),
992
      objects.NodeGroup(name="restricted",
993
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
994
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
995
                        ipolicy=objects.MakeEmptyIPolicy(),
996
                        ndparams={},
997
                        diskparams=self.custom_diskparams,
998
                        ),
999
      ]
1000
    self.cluster = objects.Cluster(cluster_name="testcluster",
1001
      hvparams=constants.HVC_DEFAULTS,
1002
      beparams={
1003
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
1004
        },
1005
      nicparams={
1006
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
1007
        },
1008
      ndparams=constants.NDC_DEFAULTS,
1009
      ipolicy=constants.IPOLICY_DEFAULTS,
1010
      diskparams=constants.DISK_DT_DEFAULTS,
1011
      )
1012

    
1013
  def _Create(self, selected):
1014
    return query.Query(query.GROUP_FIELDS, selected)
1015

    
1016
  def testSimple(self):
1017
    q = self._Create(["name", "uuid", "alloc_policy"])
1018
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1019

    
1020
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1021

    
1022
    self.assertEqual(q.Query(gqd),
1023
      [[(constants.RS_NORMAL, "default"),
1024
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1025
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1026
        ],
1027
       [(constants.RS_NORMAL, "restricted"),
1028
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1029
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1030
        ],
1031
       ])
1032

    
1033
  def testNodes(self):
1034
    groups_to_nodes = {
1035
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1036
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1037
      }
1038

    
1039
    q = self._Create(["name", "node_cnt", "node_list"])
1040
    gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1041
                               False)
1042

    
1043
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1044

    
1045
    self.assertEqual(q.Query(gqd),
1046
                     [[(constants.RS_NORMAL, "default"),
1047
                       (constants.RS_NORMAL, 2),
1048
                       (constants.RS_NORMAL, ["node1", "node2"]),
1049
                       ],
1050
                      [(constants.RS_NORMAL, "restricted"),
1051
                       (constants.RS_NORMAL, 3),
1052
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1053
                       ],
1054
                      ])
1055

    
1056
  def testInstances(self):
1057
    groups_to_instances = {
1058
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1059
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1060
      }
1061

    
1062
    q = self._Create(["pinst_cnt", "pinst_list"])
1063
    gqd = query.GroupQueryData(self.cluster, self.groups, None,
1064
      groups_to_instances, False)
1065

    
1066
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1067

    
1068
    self.assertEqual(q.Query(gqd),
1069
                     [[(constants.RS_NORMAL, 2),
1070
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
1071
                       ],
1072
                      [(constants.RS_NORMAL, 3),
1073
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1074
                       ],
1075
                      ])
1076

    
1077
  def testDiskparams(self):
1078
    q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1079
    gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1080

    
1081
    self.assertEqual(q.RequestedData(),
1082
                     set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1083

    
1084
    self.assertEqual(q.Query(gqd),
1085
      [[(constants.RS_NORMAL, "default"),
1086
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1087
        (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1088
        (constants.RS_NORMAL, {}),
1089
        ],
1090
       [(constants.RS_NORMAL, "restricted"),
1091
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1092
        (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1093
                                                     self.custom_diskparams)),
1094
        (constants.RS_NORMAL, self.custom_diskparams),
1095
        ],
1096
       ])
1097

    
1098

    
1099
class TestOsQuery(unittest.TestCase):
1100
  def _Create(self, selected):
1101
    return query.Query(query.OS_FIELDS, selected)
1102

    
1103
  def test(self):
1104
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1105
    api_versions = [10, 0, 15, 5]
1106
    parameters = ["zpar3", "apar9"]
1107

    
1108
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1109
    assert (api_versions != sorted(api_versions) and
1110
            api_versions != utils.NiceSort(variants))
1111
    assert (parameters != sorted(parameters) and
1112
            parameters != utils.NiceSort(parameters))
1113

    
1114
    data = [
1115
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1116
                   variants=set(), api_versions=set(), parameters=set(),
1117
                   node_status={ "some": "status", }),
1118
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1119
                   variants=set(variants),
1120
                   api_versions=set(api_versions),
1121
                   parameters=set(parameters),
1122
                   node_status={ "some": "other", "status": None, }),
1123
      ]
1124

    
1125

    
1126
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1127
                      "api_versions", "parameters", "node_status"])
1128
    self.assertEqual(q.RequestedData(), set([]))
1129
    self.assertEqual(q.Query(data),
1130
                     [[(constants.RS_NORMAL, "debian"),
1131
                       (constants.RS_NORMAL, False),
1132
                       (constants.RS_NORMAL, False),
1133
                       (constants.RS_NORMAL, False),
1134
                       (constants.RS_NORMAL, []),
1135
                       (constants.RS_NORMAL, []),
1136
                       (constants.RS_NORMAL, []),
1137
                       (constants.RS_NORMAL, {"some": "status"})],
1138
                      [(constants.RS_NORMAL, "dos"),
1139
                       (constants.RS_NORMAL, True),
1140
                       (constants.RS_NORMAL, False),
1141
                       (constants.RS_NORMAL, True),
1142
                       (constants.RS_NORMAL,
1143
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
1144
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
1145
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
1146
                       (constants.RS_NORMAL,
1147
                        { "some": "other", "status": None, })
1148
                       ]])
1149

    
1150

    
1151
class TestQueryFields(unittest.TestCase):
1152
  def testAllFields(self):
1153
    for fielddefs in query.ALL_FIELD_LISTS:
1154
      result = query.QueryFields(fielddefs, None)
1155
      self.assert_(isinstance(result, dict))
1156
      response = objects.QueryFieldsResponse.FromDict(result)
1157
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1158
        [(fdef2.name, fdef2.title)
1159
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1160
                                                key=lambda x: x[0].name)])
1161

    
1162
  def testSomeFields(self):
1163
    rnd = random.Random(5357)
1164

    
1165
    for _ in range(10):
1166
      for fielddefs in query.ALL_FIELD_LISTS:
1167
        if len(fielddefs) > 20:
1168
          sample_size = rnd.randint(5, 20)
1169
        else:
1170
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1171
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1172
                                                         sample_size)]
1173
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1174
        self.assert_(isinstance(result, dict))
1175
        response = objects.QueryFieldsResponse.FromDict(result)
1176
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1177
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1178

    
1179

    
1180
class TestQueryFilter(unittest.TestCase):
1181
  def testRequestedNames(self):
1182
    for (what, fielddefs) in query.ALL_FIELDS.items():
1183
      if what == constants.QR_JOB:
1184
        namefield = "id"
1185
        nameval = 123
1186
        namevalempty = 0
1187
        genval = lambda i: i * 10
1188
        randvals = [17361, 22015, 13193, 15215]
1189
      else:
1190
        nameval = "abc"
1191
        namevalempty = ""
1192
        genval = lambda i: "x%s" % i
1193
        randvals = ["x17361", "x22015", "x13193", "x15215"]
1194
        if what == constants.QR_EXPORT:
1195
          namefield = "export"
1196
        else:
1197
          namefield = "name"
1198

    
1199
      assert namefield in fielddefs
1200

    
1201
      reqnames = [genval(i) for i in range(4)]
1202
      innerfilter = [["=", namefield, v] for v in reqnames]
1203

    
1204
      # No name field
1205
      q = query.Query(fielddefs, [namefield],
1206
                      qfilter=["=", namefield, nameval], namefield=None)
1207
      self.assertEqual(q.RequestedNames(), None)
1208

    
1209
      # No filter
1210
      q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1211
      self.assertEqual(q.RequestedNames(), None)
1212

    
1213
      # Check empty query
1214
      q = query.Query(fielddefs, [namefield], qfilter=["|"],
1215
                      namefield=namefield)
1216
      self.assertEqual(q.RequestedNames(), None)
1217

    
1218
      # Check order
1219
      q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1220
                      namefield=namefield)
1221
      self.assertEqual(q.RequestedNames(), reqnames)
1222

    
1223
      # Check reverse order
1224
      q = query.Query(fielddefs, [namefield],
1225
                      qfilter=["|"] + list(reversed(innerfilter)),
1226
                      namefield=namefield)
1227
      self.assertEqual(q.RequestedNames(), list(reversed(reqnames)))
1228

    
1229
      # Duplicates
1230
      q = query.Query(fielddefs, [namefield],
1231
                      qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1232
                      namefield=namefield)
1233
      self.assertEqual(q.RequestedNames(), reqnames)
1234

    
1235
      # Unknown name field
1236
      self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1237
                        namefield="_unknown_field_")
1238

    
1239
      # Filter with AND
1240
      q = query.Query(fielddefs, [namefield],
1241
                      qfilter=["|", ["=", namefield, nameval],
1242
                                    ["&", ["=", namefield, namevalempty]]],
1243
                      namefield=namefield)
1244
      self.assertTrue(q.RequestedNames() is None)
1245

    
1246
      # Filter with NOT
1247
      q = query.Query(fielddefs, [namefield],
1248
                      qfilter=["|", ["=", namefield, nameval],
1249
                                    ["!", ["=", namefield, namevalempty]]],
1250
                      namefield=namefield)
1251
      self.assertTrue(q.RequestedNames() is None)
1252

    
1253
      # Filter with only OR (names must be in correct order)
1254
      q = query.Query(fielddefs, [namefield],
1255
                      qfilter=["|", ["=", namefield, randvals[0]],
1256
                                    ["|", ["=", namefield, randvals[1]]],
1257
                                    ["|", ["|", ["=", namefield, randvals[2]]]],
1258
                                    ["=", namefield, randvals[3]]],
1259
                      namefield=namefield)
1260
      self.assertEqual(q.RequestedNames(), randvals)
1261

    
1262
  @staticmethod
1263
  def _GenNestedFilter(namefield, op, depth, nameval):
1264
    nested = ["=", namefield, nameval]
1265
    for i in range(depth):
1266
      nested = [op, nested]
1267
    return nested
1268

    
1269
  def testCompileFilter(self):
1270
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1271

    
1272
    for (what, fielddefs) in query.ALL_FIELDS.items():
1273
      if what == constants.QR_JOB:
1274
        namefield = "id"
1275
        nameval = 123
1276
      elif what == constants.QR_EXPORT:
1277
        namefield = "export"
1278
        nameval = "value"
1279
      else:
1280
        namefield = "name"
1281
        nameval = "value"
1282

    
1283
      checks = [
1284
        [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1285
        ["=", "_unknown_field", "value"],
1286
        self._GenNestedFilter(namefield, "|", levels_max, nameval),
1287
        self._GenNestedFilter(namefield, "|", levels_max * 3, nameval),
1288
        self._GenNestedFilter(namefield, "!", levels_max, nameval),
1289
        ]
1290

    
1291
      for qfilter in checks:
1292
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1293
                          fielddefs, None, qfilter)
1294

    
1295
      for op in ["|", "!"]:
1296
        qfilter = self._GenNestedFilter(namefield, op, levels_max - 1, nameval)
1297
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1298
                                                      qfilter)))
1299

    
1300
  def testQueryInputOrder(self):
1301
    fielddefs = query._PrepareFieldList([
1302
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1303
       None, 0, lambda ctx, item: item["pnode"]),
1304
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1305
       None, 0, lambda ctx, item: item["snode"]),
1306
      ], [])
1307

    
1308
    data = [
1309
      { "pnode": "node1", "snode": "node44", },
1310
      { "pnode": "node30", "snode": "node90", },
1311
      { "pnode": "node25", "snode": "node1", },
1312
      { "pnode": "node20", "snode": "node1", },
1313
      ]
1314

    
1315
    qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1316

    
1317
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1318
                    qfilter=qfilter)
1319
    self.assertTrue(q.RequestedNames() is None)
1320
    self.assertFalse(q.RequestedData())
1321
    self.assertEqual(q.Query(data),
1322
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1323
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1324
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1325

    
1326
    # Try again with reversed input data
1327
    self.assertEqual(q.Query(reversed(data)),
1328
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1329
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1330
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1331

    
1332
    # No name field, result must be in incoming order
1333
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1334
                    qfilter=qfilter)
1335
    self.assertFalse(q.RequestedData())
1336
    self.assertEqual(q.Query(data),
1337
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1338
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1339
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1340
    self.assertEqual(q.OldStyleQuery(data), [
1341
      ["node1", "node44"],
1342
      ["node25", "node1"],
1343
      ["node20", "node1"],
1344
      ])
1345
    self.assertEqual(q.Query(reversed(data)),
1346
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1347
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1348
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1349
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1350
      ["node20", "node1"],
1351
      ["node25", "node1"],
1352
      ["node1", "node44"],
1353
      ])
1354

    
1355
    # Name field, but no sorting, result must be in incoming order
1356
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1357
    self.assertFalse(q.RequestedData())
1358
    self.assertEqual(q.Query(data, sort_by_name=False),
1359
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1360
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1361
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1362
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1363
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1364
      ["node1", "node44"],
1365
      ["node30", "node90"],
1366
      ["node25", "node1"],
1367
      ["node20", "node1"],
1368
      ])
1369
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1370
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1371
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1372
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1373
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1374
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1375
      ["node20", "node1"],
1376
      ["node25", "node1"],
1377
      ["node30", "node90"],
1378
      ["node1", "node44"],
1379
      ])
1380

    
1381
  def testEqualNamesOrder(self):
1382
    fielddefs = query._PrepareFieldList([
1383
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1384
       None, 0, lambda ctx, item: item["pnode"]),
1385
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1386
       None, 0, lambda ctx, item: item["num"]),
1387
      ], [])
1388

    
1389
    data = [
1390
      { "pnode": "node1", "num": 100, },
1391
      { "pnode": "node1", "num": 25, },
1392
      { "pnode": "node2", "num": 90, },
1393
      { "pnode": "node2", "num": 30, },
1394
      ]
1395

    
1396
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1397
                    qfilter=["|", ["=", "pnode", "node1"],
1398
                                  ["=", "pnode", "node2"],
1399
                                  ["=", "pnode", "node1"]])
1400
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1401
                     msg="Did not return unique names")
1402
    self.assertFalse(q.RequestedData())
1403
    self.assertEqual(q.Query(data),
1404
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1405
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1406
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1407
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1408
    self.assertEqual(q.Query(data, sort_by_name=False),
1409
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1410
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1411
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1412
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1413

    
1414
    data = [
1415
      { "pnode": "nodeX", "num": 50, },
1416
      { "pnode": "nodeY", "num": 40, },
1417
      { "pnode": "nodeX", "num": 30, },
1418
      { "pnode": "nodeX", "num": 20, },
1419
      { "pnode": "nodeM", "num": 10, },
1420
      ]
1421

    
1422
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1423
                    qfilter=["|", ["=", "pnode", "nodeX"],
1424
                                  ["=", "pnode", "nodeY"],
1425
                                  ["=", "pnode", "nodeY"],
1426
                                  ["=", "pnode", "nodeY"],
1427
                                  ["=", "pnode", "nodeM"]])
1428
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1429
                     msg="Did not return unique names")
1430
    self.assertFalse(q.RequestedData())
1431

    
1432
    # First sorted by name, then input order
1433
    self.assertEqual(q.Query(data, sort_by_name=True),
1434
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1435
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1436
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1437
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1438
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1439

    
1440
    # Input order
1441
    self.assertEqual(q.Query(data, sort_by_name=False),
1442
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1443
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1444
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1445
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1446
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1447

    
1448
  def testFilter(self):
1449
    (DK_A, DK_B) = range(1000, 1002)
1450

    
1451
    fielddefs = query._PrepareFieldList([
1452
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1453
       DK_A, 0, lambda ctx, item: item["name"]),
1454
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1455
       DK_B, 0, lambda ctx, item: item["other"]),
1456
      ], [])
1457

    
1458
    data = [
1459
      { "name": "node1", "other": "foo", },
1460
      { "name": "node2", "other": "bar", },
1461
      { "name": "node3", "other": "Hello", },
1462
      ]
1463

    
1464
    # Empty filter
1465
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1466
                    qfilter=["|"])
1467
    self.assertTrue(q.RequestedNames() is None)
1468
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1469
    self.assertEqual(q.Query(data), [])
1470

    
1471
    # Normal filter
1472
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1473
                    qfilter=["=", "name", "node1"])
1474
    self.assertEqual(q.RequestedNames(), ["node1"])
1475
    self.assertEqual(q.Query(data),
1476
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1477

    
1478
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1479
                    qfilter=(["|", ["=", "name", "node1"],
1480
                                   ["=", "name", "node3"]]))
1481
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1482
    self.assertEqual(q.Query(data),
1483
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1484
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1485

    
1486
    # Complex filter
1487
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1488
                    qfilter=(["|", ["=", "name", "node1"],
1489
                                   ["|", ["=", "name", "node3"],
1490
                                         ["=", "name", "node2"]],
1491
                                   ["=", "name", "node3"]]))
1492
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1493
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1494
    self.assertEqual(q.Query(data),
1495
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1496
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1497
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1498

    
1499
    # Filter data type mismatch
1500
    for i in [-1, 0, 1, 123, [], None, True, False]:
1501
      self.assertRaises(errors.ParameterError, query.Query,
1502
                        fielddefs, ["name", "other"], namefield="name",
1503
                        qfilter=["=", "name", i])
1504

    
1505
    # Negative filter
1506
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1507
                    qfilter=["!", ["|", ["=", "name", "node1"],
1508
                                        ["=", "name", "node3"]]])
1509
    self.assertTrue(q.RequestedNames() is None)
1510
    self.assertEqual(q.Query(data),
1511
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1512

    
1513
    # Not equal
1514
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1515
                    qfilter=["!=", "name", "node3"])
1516
    self.assertTrue(q.RequestedNames() is None)
1517
    self.assertEqual(q.Query(data),
1518
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1519
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1520

    
1521
    # Data type
1522
    q = query.Query(fielddefs, [], namefield="name",
1523
                    qfilter=["|", ["=", "other", "bar"],
1524
                                  ["=", "name", "foo"]])
1525
    self.assertTrue(q.RequestedNames() is None)
1526
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1527
    self.assertEqual(q.Query(data), [[]])
1528

    
1529
    # Only one data type
1530
    q = query.Query(fielddefs, ["other"], namefield="name",
1531
                    qfilter=["=", "other", "bar"])
1532
    self.assertTrue(q.RequestedNames() is None)
1533
    self.assertEqual(q.RequestedData(), set([DK_B]))
1534
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1535

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

    
1542
    # Data type in boolean operator
1543
    q = query.Query(fielddefs, [], namefield="name",
1544
                    qfilter=["?", "name"])
1545
    self.assertTrue(q.RequestedNames() is None)
1546
    self.assertEqual(q.RequestedData(), set([DK_A]))
1547
    self.assertEqual(q.Query(data), [[], [], []])
1548

    
1549
    q = query.Query(fielddefs, [], namefield="name",
1550
                    qfilter=["!", ["?", "name"]])
1551
    self.assertTrue(q.RequestedNames() is None)
1552
    self.assertEqual(q.RequestedData(), set([DK_A]))
1553
    self.assertEqual(q.Query(data), [])
1554

    
1555
  def testFilterContains(self):
1556
    fielddefs = query._PrepareFieldList([
1557
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1558
       None, 0, lambda ctx, item: item["name"]),
1559
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1560
       None, 0, lambda ctx, item: item["other"]),
1561
      ], [])
1562

    
1563
    data = [
1564
      { "name": "node2", "other": ["x", "y", "bar"], },
1565
      { "name": "node3", "other": "Hello", },
1566
      { "name": "node1", "other": ["a", "b", "foo"], },
1567
      { "name": "empty", "other": []},
1568
      ]
1569

    
1570
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1571
                    qfilter=["=[]", "other", "bar"])
1572
    self.assertTrue(q.RequestedNames() is None)
1573
    self.assertEqual(q.Query(data), [
1574
      [(constants.RS_NORMAL, "node2"),
1575
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1576
      ])
1577

    
1578
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1579
                    qfilter=["|", ["=[]", "other", "bar"],
1580
                                  ["=[]", "other", "a"],
1581
                                  ["=[]", "other", "b"]])
1582
    self.assertTrue(q.RequestedNames() is None)
1583
    self.assertEqual(q.Query(data), [
1584
      [(constants.RS_NORMAL, "node1"),
1585
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1586
      [(constants.RS_NORMAL, "node2"),
1587
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1588
      ])
1589
    self.assertEqual(q.OldStyleQuery(data), [
1590
      ["node1", ["a", "b", "foo"]],
1591
      ["node2", ["x", "y", "bar"]],
1592
      ])
1593

    
1594
    # Boolean test
1595
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1596
                    qfilter=["?", "other"])
1597
    self.assertEqual(q.OldStyleQuery(data), [
1598
      ["node1", ["a", "b", "foo"]],
1599
      ["node2", ["x", "y", "bar"]],
1600
      ["node3", "Hello"],
1601
      ])
1602

    
1603
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1604
                    qfilter=["!", ["?", "other"]])
1605
    self.assertEqual(q.OldStyleQuery(data), [
1606
      ["empty", []],
1607
      ])
1608

    
1609
  def testFilterHostname(self):
1610
    fielddefs = query._PrepareFieldList([
1611
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1612
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1613
      ], [])
1614

    
1615
    data = [
1616
      { "name": "node1.example.com", },
1617
      { "name": "node2.example.com", },
1618
      { "name": "node2.example.net", },
1619
      ]
1620

    
1621
    q = query.Query(fielddefs, ["name"], namefield="name",
1622
                    qfilter=["=", "name", "node2"])
1623
    self.assertEqual(q.RequestedNames(), ["node2"])
1624
    self.assertEqual(q.Query(data), [
1625
      [(constants.RS_NORMAL, "node2.example.com")],
1626
      [(constants.RS_NORMAL, "node2.example.net")],
1627
      ])
1628

    
1629
    q = query.Query(fielddefs, ["name"], namefield="name",
1630
                    qfilter=["=", "name", "node1"])
1631
    self.assertEqual(q.RequestedNames(), ["node1"])
1632
    self.assertEqual(q.Query(data), [
1633
      [(constants.RS_NORMAL, "node1.example.com")],
1634
      ])
1635

    
1636
    q = query.Query(fielddefs, ["name"], namefield="name",
1637
                    qfilter=["=", "name", "othername"])
1638
    self.assertEqual(q.RequestedNames(), ["othername"])
1639
    self.assertEqual(q.Query(data), [])
1640

    
1641
    q = query.Query(fielddefs, ["name"], namefield="name",
1642
                    qfilter=["|", ["=", "name", "node1.example.com"],
1643
                                  ["=", "name", "node2"]])
1644
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1645
    self.assertEqual(q.Query(data), [
1646
      [(constants.RS_NORMAL, "node1.example.com")],
1647
      [(constants.RS_NORMAL, "node2.example.com")],
1648
      [(constants.RS_NORMAL, "node2.example.net")],
1649
      ])
1650
    self.assertEqual(q.OldStyleQuery(data), [
1651
      ["node1.example.com"],
1652
      ["node2.example.com"],
1653
      ["node2.example.net"],
1654
      ])
1655

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

    
1668
  def testFilterBoolean(self):
1669
    fielddefs = query._PrepareFieldList([
1670
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1671
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1672
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1673
       None, 0, lambda ctx, item: item["value"]),
1674
      ], [])
1675

    
1676
    data = [
1677
      { "name": "node1", "value": False, },
1678
      { "name": "node2", "value": True, },
1679
      { "name": "node3", "value": True, },
1680
      ]
1681

    
1682
    q = query.Query(fielddefs, ["name", "value"],
1683
                    qfilter=["|", ["=", "value", False],
1684
                                  ["=", "value", True]])
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
    q = query.Query(fielddefs, ["name", "value"],
1693
                    qfilter=["|", ["=", "value", False],
1694
                                  ["!", ["=", "value", False]]])
1695
    self.assertTrue(q.RequestedNames() is None)
1696
    self.assertEqual(q.Query(data), [
1697
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1698
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1699
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1700
      ])
1701

    
1702
    # Comparing bool with string
1703
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1704
      self.assertRaises(errors.ParameterError, query.Query,
1705
                        fielddefs, ["name", "value"],
1706
                        qfilter=["=", "value", i])
1707

    
1708
    # Truth filter
1709
    q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1710
    self.assertTrue(q.RequestedNames() is None)
1711
    self.assertEqual(q.Query(data), [
1712
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1713
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1714
      ])
1715

    
1716
    # Negative bool filter
1717
    q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1718
    self.assertTrue(q.RequestedNames() is None)
1719
    self.assertEqual(q.Query(data), [
1720
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1721
      ])
1722

    
1723
    # Complex truth filter
1724
    q = query.Query(fielddefs, ["name", "value"],
1725
                    qfilter=["|", ["&", ["=", "name", "node1"],
1726
                                        ["!", ["?", "value"]]],
1727
                                  ["?", "value"]])
1728
    self.assertTrue(q.RequestedNames() is None)
1729
    self.assertEqual(q.Query(data), [
1730
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1731
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1732
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1733
      ])
1734

    
1735
  def testFilterRegex(self):
1736
    fielddefs = query._PrepareFieldList([
1737
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1738
       None, 0, lambda ctx, item: item["name"]),
1739
      ], [])
1740

    
1741
    data = [
1742
      { "name": "node1.example.com", },
1743
      { "name": "node2.site.example.com", },
1744
      { "name": "node2.example.net", },
1745

    
1746
      # Empty name
1747
      { "name": "", },
1748
      ]
1749

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

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

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

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

    
1782
    q = query.Query(fielddefs, ["name"], namefield="name",
1783
                    qfilter=["=~", "name", r"^$"])
1784
    self.assertTrue(q.RequestedNames() is None)
1785
    self.assertEqual(q.Query(data), [
1786
      [(constants.RS_NORMAL, "")],
1787
      ])
1788

    
1789
    # Invalid regular expression
1790
    self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1791
                      qfilter=["=~", "name", r"["])
1792

    
1793
  def testFilterLessGreater(self):
1794
    fielddefs = query._PrepareFieldList([
1795
      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1796
       None, 0, lambda ctx, item: item),
1797
      ], [])
1798

    
1799
    data = range(100)
1800

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

    
1807
    q = query.Query(fielddefs, ["value"],
1808
                    qfilter=["<=", "value", 30])
1809
    self.assertTrue(q.RequestedNames() is None)
1810
    self.assertEqual(q.Query(data),
1811
                     [[(constants.RS_NORMAL, i)] for i in range(31)])
1812

    
1813
    q = query.Query(fielddefs, ["value"],
1814
                    qfilter=[">", "value", 40])
1815
    self.assertTrue(q.RequestedNames() is None)
1816
    self.assertEqual(q.Query(data),
1817
                     [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
1818

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

    
1825
  def testFilterLessGreaterJobId(self):
1826
    fielddefs = query._PrepareFieldList([
1827
      (query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"),
1828
       None, query.QFF_JOB_ID, lambda ctx, item: item),
1829
      ], [])
1830

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

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

    
1835
    q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"])
1836
    self.assertTrue(q.RequestedNames() is None)
1837
    self.assertEqual(q.Query(data), [
1838
      [(constants.RS_NORMAL, "1")],
1839
      [(constants.RS_NORMAL, "2")],
1840
      [(constants.RS_NORMAL, "3")],
1841
      [(constants.RS_NORMAL, "10")],
1842
      [(constants.RS_NORMAL, "15")],
1843
      [(constants.RS_NORMAL, "7")],
1844
      ])
1845

    
1846
    q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"])
1847
    self.assertTrue(q.RequestedNames() is None)
1848
    self.assertEqual(q.Query(data), [
1849
      [(constants.RS_NORMAL, "102")],
1850
      [(constants.RS_NORMAL, "120")],
1851
      [(constants.RS_NORMAL, "125")],
1852
      [(constants.RS_NORMAL, "100")],
1853
      ])
1854

    
1855
    # Integers are no valid job IDs
1856
    self.assertRaises(errors.ParameterError, query.Query,
1857
                      fielddefs, ["id"], qfilter=[">=", "id", 10])
1858

    
1859
  def testFilterLessGreaterSplitTimestamp(self):
1860
    fielddefs = query._PrepareFieldList([
1861
      (query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"),
1862
       None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item),
1863
      ], [])
1864

    
1865
    data = [
1866
      utils.SplitTime(0),
1867
      utils.SplitTime(0.1),
1868
      utils.SplitTime(18224.7872),
1869
      utils.SplitTime(919896.12623),
1870
      utils.SplitTime(999),
1871
      utils.SplitTime(989.9999),
1872
      ]
1873

    
1874
    for i in [0, [0, 0]]:
1875
      q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i])
1876
      self.assertTrue(q.RequestedNames() is None)
1877
      self.assertEqual(q.Query(data), [])
1878

    
1879
    q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000])
1880
    self.assertTrue(q.RequestedNames() is None)
1881
    self.assertEqual(q.Query(data), [
1882
      [(constants.RS_NORMAL, (0, 0))],
1883
      [(constants.RS_NORMAL, (0, 100000))],
1884
      [(constants.RS_NORMAL, (999, 0))],
1885
      [(constants.RS_NORMAL, (989, 999900))],
1886
      ])
1887

    
1888
    q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3])
1889
    self.assertTrue(q.RequestedNames() is None)
1890
    self.assertEqual(q.Query(data), [
1891
      [(constants.RS_NORMAL, (18224, 787200))],
1892
      [(constants.RS_NORMAL, (919896, 126230))],
1893
      ])
1894

    
1895
    for i in [18224.7772, utils.SplitTime(18224.7772)]:
1896
      q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i])
1897
      self.assertTrue(q.RequestedNames() is None)
1898
      self.assertEqual(q.Query(data), [
1899
        [(constants.RS_NORMAL, (18224, 787200))],
1900
        [(constants.RS_NORMAL, (919896, 126230))],
1901
        ])
1902

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

    
1909

    
1910
if __name__ == "__main__":
1911
  testutils.GanetiTestProgram()