Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (74.4 kB)

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

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

    
21

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

    
24
import re
25
import unittest
26
import random
27

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

    
36
import ganeti.masterd.instance as gmi
37

    
38
import testutils
39

    
40

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

    
46

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

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

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

    
57

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

    
65

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
298

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

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

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

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

    
321

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
478
    master_node.group = ng_uuid
479

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
604

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
868
    tested_status = set()
869

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
989

    
990
class TestGroupQuery(unittest.TestCase):
991

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1113

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

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

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

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

    
1140

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

    
1165

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

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

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

    
1194

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

    
1214
      assert namefield in fielddefs
1215

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1814
    data = range(100)
1815

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1924

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