Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ be3a4b14

History | View | Annotate | Download (60.3 kB)

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

    
4
# Copyright (C) 2010, 2011 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 testutils
37

    
38

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

    
44

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

    
49
    for name, value in kwargs.items():
50
      setattr(self, name, value)
51

    
52
  def __iter__(self):
53
    return iter(self.data)
54

    
55

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

    
63

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
225
  def testUnknown(self):
226
    fielddef = query._PrepareFieldList([
227
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
228
       None, 0, lambda _, item: "name%s" % item),
229
      (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP, "Other"),
230
       None, 0, lambda *args: 1234),
231
      (query._MakeField("nodata", "NoData", constants.QFT_NUMBER, "No data"),
232
       None, 0, lambda *args: query._FS_NODATA ),
233
      (query._MakeField("unavail", "Unavail", constants.QFT_BOOL, "Unavail"),
234
       None, 0, lambda *args: query._FS_UNAVAIL),
235
      ], [])
236

    
237
    for selected in [["foo"], ["Hello", "World"],
238
                     ["name1", "other", "foo"]]:
239
      q = query.Query(fielddef, selected)
240
      self.assertEqual(len(q._fields), len(selected))
241
      self.assert_(compat.all(len(row) == len(selected)
242
                              for row in q.Query(_QueryData(range(1, 10)))))
243
      self.assertEqual(q.Query(_QueryData(range(1, 10))),
244
                       [[(constants.RS_UNKNOWN, None)] * len(selected)
245
                        for i in range(1, 10)])
246
      self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
247
                       [{ "name": name, "title": name,
248
                          "kind": constants.QFT_UNKNOWN,
249
                          "doc": "Unknown field '%s'" % name}
250
                        for name in selected])
251

    
252
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
253
    self.assertEqual(len(q._fields), 4)
254
    self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
255
                     ["name%s" % i, 1234, None, None]
256
                     for i in range(1, 10)
257
                     ])
258

    
259
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
260
    self.assertEqual(len(q._fields), 5)
261
    self.assertEqual(q.Query(_QueryData(range(1, 10))),
262
                     [[(constants.RS_NORMAL, "name%s" % i),
263
                       (constants.RS_NORMAL, 1234),
264
                       (constants.RS_NODATA, None),
265
                       (constants.RS_UNAVAIL, None),
266
                       (constants.RS_UNKNOWN, None)]
267
                      for i in range(1, 10)])
268

    
269
  def testAliases(self):
270
    fields = [
271
      (query._MakeField("a", "a-title", constants.QFT_TEXT, "Field A"),
272
       None, 0, lambda *args: None),
273
      (query._MakeField("b", "b-title", constants.QFT_TEXT, "Field B"),
274
       None, 0, lambda *args: None),
275
      ]
276
    # duplicate field
277
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
278
                      [("b", "a")])
279
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
280
                      [("c", "b"), ("c", "a")])
281
    # missing target
282
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
283
                      [("c", "d")])
284
    fdefs = query._PrepareFieldList(fields, [("c", "b")])
285
    self.assertEqual(len(fdefs), 3)
286
    self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
287

    
288

    
289
class TestGetNodeRole(unittest.TestCase):
290
  def test(self):
291
    tested_role = set()
292

    
293
    checks = [
294
      (constants.NR_MASTER, "node1", objects.Node(name="node1")),
295
      (constants.NR_MCANDIDATE, "master",
296
       objects.Node(name="node1", master_candidate=True)),
297
      (constants.NR_REGULAR, "master", objects.Node(name="node1")),
298
      (constants.NR_DRAINED, "master",
299
       objects.Node(name="node1", drained=True)),
300
      (constants.NR_OFFLINE,
301
       "master", objects.Node(name="node1", offline=True)),
302
      ]
303

    
304
    for (role, master_name, node) in checks:
305
      result = query._GetNodeRole(node, master_name)
306
      self.assertEqual(result, role)
307
      tested_role.add(result)
308

    
309
    self.assertEqual(tested_role, constants.NR_ALL)
310

    
311

    
312
class TestNodeQuery(unittest.TestCase):
313
  def _Create(self, selected):
314
    return query.Query(query.NODE_FIELDS, selected)
315

    
316
  def testSimple(self):
317
    nodes = [
318
      objects.Node(name="node1", drained=False),
319
      objects.Node(name="node2", drained=True),
320
      objects.Node(name="node3", drained=False),
321
      ]
322
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
323
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None,
324
                                None)
325

    
326
      q = self._Create(["name", "drained"])
327
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
328
      self.assertEqual(q.Query(nqd),
329
                       [[(constants.RS_NORMAL, "node1"),
330
                         (constants.RS_NORMAL, False)],
331
                        [(constants.RS_NORMAL, "node2"),
332
                         (constants.RS_NORMAL, True)],
333
                        [(constants.RS_NORMAL, "node3"),
334
                         (constants.RS_NORMAL, False)],
335
                       ])
336
      self.assertEqual(q.OldStyleQuery(nqd),
337
                       [["node1", False],
338
                        ["node2", True],
339
                        ["node3", False]])
340

    
341
  def test(self):
342
    selected = query.NODE_FIELDS.keys()
343
    field_index = dict((field, idx) for idx, field in enumerate(selected))
344

    
345
    q = self._Create(selected)
346
    self.assertEqual(q.RequestedData(),
347
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
348
                          query.NQ_GROUP, query.NQ_OOB]))
349

    
350
    cluster = objects.Cluster(cluster_name="testcluster",
351
      hvparams=constants.HVC_DEFAULTS,
352
      beparams={
353
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
354
        },
355
      nicparams={
356
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
357
        },
358
      ndparams=constants.NDC_DEFAULTS,
359
        )
360

    
361
    node_names = ["node%s" % i for i in range(20)]
362
    master_name = node_names[3]
363
    nodes = [
364
      objects.Node(name=name,
365
                   primary_ip="192.0.2.%s" % idx,
366
                   secondary_ip="192.0.100.%s" % idx,
367
                   serial_no=7789 * idx,
368
                   master_candidate=(name != master_name and idx % 3 == 0),
369
                   offline=False,
370
                   drained=False,
371
                   powered=True,
372
                   vm_capable=True,
373
                   master_capable=False,
374
                   ndparams={},
375
                   group="default",
376
                   ctime=1290006900,
377
                   mtime=1290006913,
378
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
379
      for idx, name in enumerate(node_names)
380
      ]
381

    
382
    master_node = nodes[3]
383
    master_node.AddTag("masternode")
384
    master_node.AddTag("another")
385
    master_node.AddTag("tag")
386
    master_node.ctime = None
387
    master_node.mtime = None
388
    assert master_node.name == master_name
389

    
390
    live_data_name = node_names[4]
391
    assert live_data_name != master_name
392

    
393
    fake_live_data = {
394
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
395
      "cnodes": 4,
396
      "csockets": 4,
397
      "ctotal": 8,
398
      "mnode": 128,
399
      "mfree": 100,
400
      "mtotal": 4096,
401
      "dfree": 5 * 1024 * 1024,
402
      "dtotal": 100 * 1024 * 1024,
403
      }
404

    
405
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
406
            sorted(fake_live_data.keys()))
407

    
408
    live_data = dict.fromkeys(node_names, {})
409
    live_data[live_data_name] = \
410
      dict((query._NODE_LIVE_FIELDS[name][2], value)
411
           for name, value in fake_live_data.items())
412

    
413
    node_to_primary = dict((name, set()) for name in node_names)
414
    node_to_primary[master_name].update(["inst1", "inst2"])
415

    
416
    node_to_secondary = dict((name, set()) for name in node_names)
417
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
418

    
419
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
420
    groups = {
421
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
422
      }
423

    
424
    oob_not_powered_node = node_names[0]
425
    nodes[0].powered = False
426
    oob_support = dict((name, False) for name in node_names)
427
    oob_support[master_name] = True
428
    oob_support[oob_not_powered_node] = True
429

    
430
    master_node.group = ng_uuid
431

    
432
    nqd = query.NodeQueryData(nodes, live_data, master_name,
433
                              node_to_primary, node_to_secondary, groups,
434
                              oob_support, cluster)
435
    result = q.Query(nqd)
436
    self.assert_(compat.all(len(row) == len(selected) for row in result))
437
    self.assertEqual([row[field_index["name"]] for row in result],
438
                     [(constants.RS_NORMAL, name) for name in node_names])
439

    
440
    node_to_row = dict((row[field_index["name"]][1], idx)
441
                       for idx, row in enumerate(result))
442

    
443
    master_row = result[node_to_row[master_name]]
444
    self.assert_(master_row[field_index["master"]])
445
    self.assert_(master_row[field_index["role"]], "M")
446
    self.assertEqual(master_row[field_index["group"]],
447
                     (constants.RS_NORMAL, "ng1"))
448
    self.assertEqual(master_row[field_index["group.uuid"]],
449
                     (constants.RS_NORMAL, ng_uuid))
450
    self.assertEqual(master_row[field_index["ctime"]],
451
                     (constants.RS_UNAVAIL, None))
452
    self.assertEqual(master_row[field_index["mtime"]],
453
                     (constants.RS_UNAVAIL, None))
454

    
455
    self.assert_(row[field_index["pip"]] == node.primary_ip and
456
                 row[field_index["sip"]] == node.secondary_ip and
457
                 set(row[field_index["tags"]]) == node.GetTags() and
458
                 row[field_index["serial_no"]] == node.serial_no and
459
                 row[field_index["role"]] == query._GetNodeRole(node,
460
                                                                master_name) and
461
                 (node.name == master_name or
462
                  (row[field_index["group"]] == "<unknown>" and
463
                   row[field_index["group.uuid"]] is None and
464
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
465
                                                 node.ctime) and
466
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
467
                                                 node.mtime) and
468
                   row[field_index["powered"]] == (constants.RS_NORMAL,
469
                                                   True))) or
470
                 (node.name == oob_not_powered_node and
471
                  row[field_index["powered"]] == (constants.RS_NORMAL,
472
                                                  False)) or
473
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
474
                 for row, node in zip(result, nodes))
475

    
476
    live_data_row = result[node_to_row[live_data_name]]
477

    
478
    for (field, value) in fake_live_data.items():
479
      self.assertEqual(live_data_row[field_index[field]],
480
                       (constants.RS_NORMAL, value))
481

    
482
    self.assertEqual(master_row[field_index["pinst_cnt"]],
483
                     (constants.RS_NORMAL, 2))
484
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
485
                     (constants.RS_NORMAL, 3))
486
    self.assertEqual(master_row[field_index["pinst_list"]],
487
                     (constants.RS_NORMAL,
488
                      list(node_to_primary[master_name])))
489
    self.assertEqual(live_data_row[field_index["sinst_list"]],
490
                     (constants.RS_NORMAL,
491
                      list(node_to_secondary[live_data_name])))
492

    
493
  def testGetLiveNodeField(self):
494
    nodes = [
495
      objects.Node(name="node1", drained=False, offline=False,
496
                   vm_capable=True),
497
      objects.Node(name="node2", drained=True, offline=False,
498
                   vm_capable=True),
499
      objects.Node(name="node3", drained=False, offline=False,
500
                   vm_capable=True),
501
      objects.Node(name="node4", drained=False, offline=True,
502
                   vm_capable=True),
503
      objects.Node(name="node5", drained=False, offline=False,
504
                   vm_capable=False),
505
      ]
506
    live_data = dict.fromkeys([node.name for node in nodes], {})
507

    
508
    # No data
509
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
510
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
511
                                             nqd, nodes[0]),
512
                     query._FS_NODATA)
513

    
514
    # Missing field
515
    ctx = _QueryData(None, curlive_data={
516
      "some": 1,
517
      "other": 2,
518
      })
519
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
520
                                             ctx, nodes[0]),
521
                     query._FS_UNAVAIL)
522

    
523
    # Wrong format/datatype
524
    ctx = _QueryData(None, curlive_data={
525
      "hello": ["Hello World"],
526
      "other": 2,
527
      })
528
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
529
                                             ctx, nodes[0]),
530
                     query._FS_UNAVAIL)
531

    
532
    # Offline node
533
    assert nodes[3].offline
534
    ctx = _QueryData(None, curlive_data={})
535
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
536
                                             ctx, nodes[3]),
537
                     query._FS_OFFLINE, None)
538

    
539
    # Wrong field type
540
    ctx = _QueryData(None, curlive_data={"hello": 123})
541
    self.assertRaises(AssertionError, query._GetLiveNodeField,
542
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
543

    
544
    # Non-vm_capable node
545
    assert not nodes[4].vm_capable
546
    ctx = _QueryData(None, curlive_data={})
547
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
548
                                             ctx, nodes[4]),
549
                     query._FS_UNAVAIL, None)
550

    
551

    
552
class TestInstanceQuery(unittest.TestCase):
553
  def _Create(self, selected):
554
    return query.Query(query.INSTANCE_FIELDS, selected)
555

    
556
  def testSimple(self):
557
    q = self._Create(["name", "be/memory", "ip"])
558
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
559

    
560
    cluster = objects.Cluster(cluster_name="testcluster",
561
      hvparams=constants.HVC_DEFAULTS,
562
      beparams={
563
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
564
        },
565
      nicparams={
566
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
567
        })
568

    
569
    instances = [
570
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
571
      objects.Instance(name="inst2", hvparams={}, nics=[],
572
        beparams={
573
          constants.BE_MEMORY: 512,
574
        }),
575
      objects.Instance(name="inst3", hvparams={}, beparams={},
576
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
577
      ]
578

    
579
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
580
                                  set(), {})
581
    self.assertEqual(q.Query(iqd),
582
      [[(constants.RS_NORMAL, "inst1"),
583
        (constants.RS_NORMAL, 128),
584
        (constants.RS_UNAVAIL, None),
585
       ],
586
       [(constants.RS_NORMAL, "inst2"),
587
        (constants.RS_NORMAL, 512),
588
        (constants.RS_UNAVAIL, None),
589
       ],
590
       [(constants.RS_NORMAL, "inst3"),
591
        (constants.RS_NORMAL, 128),
592
        (constants.RS_NORMAL, "192.0.2.99"),
593
       ]])
594
    self.assertEqual(q.OldStyleQuery(iqd),
595
      [["inst1", 128, None],
596
       ["inst2", 512, None],
597
       ["inst3", 128, "192.0.2.99"]])
598

    
599
  def test(self):
600
    selected = query.INSTANCE_FIELDS.keys()
601
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
602

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

    
606
    q = self._Create(selected)
607
    self.assertEqual(q.RequestedData(),
608
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
609
                          query.IQ_CONSOLE]))
610

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

    
622
    offline_nodes = ["nodeoff1", "nodeoff2"]
623
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
624
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
625

    
626
    instances = [
627
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
628
        uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
629
        ctime=1291244000, mtime=1291244400, serial_no=30,
630
        admin_up=True, hypervisor=constants.HT_XEN_PVM, os="linux1",
631
        primary_node="node1",
632
        disk_template=constants.DT_PLAIN,
633
        disks=[]),
634
      objects.Instance(name="inst2", hvparams={}, nics=[],
635
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
636
        ctime=1291211000, mtime=1291211077, serial_no=1,
637
        admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
638
        primary_node="node5",
639
        disk_template=constants.DT_DISKLESS,
640
        disks=[],
641
        beparams={
642
          constants.BE_MEMORY: 512,
643
        }),
644
      objects.Instance(name="inst3", hvparams={}, beparams={},
645
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
646
        ctime=1291011000, mtime=1291013000, serial_no=1923,
647
        admin_up=False, hypervisor=constants.HT_KVM, os="busybox",
648
        primary_node="node6",
649
        disk_template=constants.DT_DRBD8,
650
        disks=[],
651
        nics=[
652
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
653
                      nicparams={
654
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
655
                        }),
656
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
657
          ]),
658
      objects.Instance(name="inst4", hvparams={}, beparams={},
659
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
660
        ctime=1291244390, mtime=1291244395, serial_no=25,
661
        admin_up=False, hypervisor=constants.HT_XEN_PVM, os="linux1",
662
        primary_node="nodeoff2",
663
        disk_template=constants.DT_DRBD8,
664
        disks=[],
665
        nics=[
666
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
667
                      nicparams={
668
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
669
                        }),
670
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
671
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
672
                      nicparams={
673
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
674
                        }),
675
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
676
                      nicparams={
677
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
678
                        constants.NIC_LINK: "eth123",
679
                        }),
680
          ]),
681
      objects.Instance(name="inst5", hvparams={}, nics=[],
682
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
683
        ctime=1231211000, mtime=1261200000, serial_no=3,
684
        admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
685
        primary_node="nodebad2",
686
        disk_template=constants.DT_DISKLESS,
687
        disks=[],
688
        beparams={
689
          constants.BE_MEMORY: 512,
690
        }),
691
      objects.Instance(name="inst6", hvparams={}, nics=[],
692
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
693
        ctime=7513, mtime=11501, serial_no=13390,
694
        admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
695
        primary_node="node7",
696
        disk_template=constants.DT_DISKLESS,
697
        disks=[],
698
        beparams={
699
          constants.BE_MEMORY: 768,
700
        }),
701
      objects.Instance(name="inst7", hvparams={}, nics=[],
702
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
703
        ctime=None, mtime=None, serial_no=1947,
704
        admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
705
        primary_node="node6",
706
        disk_template=constants.DT_DISKLESS,
707
        disks=[],
708
        beparams={}),
709
      ]
710

    
711
    assert not utils.FindDuplicates(inst.name for inst in instances)
712

    
713
    instbyname = dict((inst.name, inst) for inst in instances)
714

    
715
    disk_usage = dict((inst.name,
716
                       cmdlib._ComputeDiskSize(inst.disk_template,
717
                                               [{"size": disk.size}
718
                                                for disk in inst.disks]))
719
                      for inst in instances)
720

    
721
    inst_bridges = {
722
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
723
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
724
                None, "eth123"],
725
      }
726

    
727
    live_data = {
728
      "inst2": {
729
        "vcpus": 3,
730
        },
731
      "inst4": {
732
        "memory": 123,
733
        },
734
      "inst6": {
735
        "memory": 768,
736
        },
737
      "inst7": {
738
        "vcpus": 3,
739
        },
740
      }
741
    wrongnode_inst = set(["inst7"])
742

    
743
    consinfo = dict((inst.name, None) for inst in instances)
744
    consinfo["inst7"] = \
745
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
746
                              host=instbyname["inst7"].primary_node,
747
                              user=constants.GANETI_RUNAS,
748
                              command=["hostname"]).ToDict()
749

    
750
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
751
                                  offline_nodes, bad_nodes, live_data,
752
                                  wrongnode_inst, consinfo)
753
    result = q.Query(iqd)
754
    self.assertEqual(len(result), len(instances))
755
    self.assert_(compat.all(len(row) == len(selected)
756
                            for row in result))
757

    
758
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
759
           "Offline nodes not included in bad nodes"
760

    
761
    tested_status = set()
762

    
763
    for (inst, row) in zip(instances, result):
764
      assert inst.primary_node in nodes
765

    
766
      self.assertEqual(row[fieldidx["name"]],
767
                       (constants.RS_NORMAL, inst.name))
768

    
769
      if inst.primary_node in offline_nodes:
770
        exp_status = constants.INSTST_NODEOFFLINE
771
      elif inst.primary_node in bad_nodes:
772
        exp_status = constants.INSTST_NODEDOWN
773
      elif inst.name in live_data:
774
        if inst.name in wrongnode_inst:
775
          exp_status = constants.INSTST_WRONGNODE
776
        elif inst.admin_up:
777
          exp_status = constants.INSTST_RUNNING
778
        else:
779
          exp_status = constants.INSTST_ERRORUP
780
      elif inst.admin_up:
781
        exp_status = constants.INSTST_ERRORDOWN
782
      else:
783
        exp_status = constants.INSTST_ADMINDOWN
784

    
785
      self.assertEqual(row[fieldidx["status"]],
786
                       (constants.RS_NORMAL, exp_status))
787

    
788
      (_, status) = row[fieldidx["status"]]
789
      tested_status.add(status)
790

    
791
      for (field, livefield) in [("oper_ram", "memory"),
792
                                 ("oper_vcpus", "vcpus")]:
793
        if inst.primary_node in bad_nodes:
794
          exp = (constants.RS_NODATA, None)
795
        elif inst.name in live_data:
796
          value = live_data[inst.name].get(livefield, None)
797
          if value is None:
798
            exp = (constants.RS_UNAVAIL, None)
799
          else:
800
            exp = (constants.RS_NORMAL, value)
801
        else:
802
          exp = (constants.RS_UNAVAIL, None)
803

    
804
        self.assertEqual(row[fieldidx[field]], exp)
805

    
806
      bridges = inst_bridges.get(inst.name, [])
807
      self.assertEqual(row[fieldidx["nic.bridges"]],
808
                       (constants.RS_NORMAL, bridges))
809
      if bridges:
810
        self.assertEqual(row[fieldidx["bridge"]],
811
                         (constants.RS_NORMAL, bridges[0]))
812
      else:
813
        self.assertEqual(row[fieldidx["bridge"]],
814
                         (constants.RS_UNAVAIL, None))
815

    
816
      for i in range(constants.MAX_NICS):
817
        if i < len(bridges) and bridges[i] is not None:
818
          exp = (constants.RS_NORMAL, bridges[i])
819
        else:
820
          exp = (constants.RS_UNAVAIL, None)
821
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
822

    
823
      if inst.primary_node in bad_nodes:
824
        exp = (constants.RS_NODATA, None)
825
      else:
826
        exp = (constants.RS_NORMAL, inst.name in live_data)
827
      self.assertEqual(row[fieldidx["oper_state"]], exp)
828

    
829
      usage = disk_usage[inst.name]
830
      if usage is None:
831
        usage = 0
832
      self.assertEqual(row[fieldidx["disk_usage"]],
833
                       (constants.RS_NORMAL, usage))
834

    
835
      for alias, target in [("sda_size", "disk.size/0"),
836
                            ("sdb_size", "disk.size/1"),
837
                            ("vcpus", "be/vcpus"),
838
                            ("ip", "nic.ip/0"),
839
                            ("mac", "nic.mac/0"),
840
                            ("bridge", "nic.bridge/0"),
841
                            ("nic_mode", "nic.mode/0"),
842
                            ("nic_link", "nic.link/0"),
843
                            ]:
844
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
845

    
846
      for field in ["ctime", "mtime"]:
847
        if getattr(inst, field) is None:
848
          # No ctime/mtime
849
          exp = (constants.RS_UNAVAIL, None)
850
        else:
851
          exp = (constants.RS_NORMAL, getattr(inst, field))
852
        self.assertEqual(row[fieldidx[field]], exp)
853

    
854
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
855

    
856
    # Ensure all possible status' have been tested
857
    self.assertEqual(tested_status, constants.INSTST_ALL)
858

    
859
  def _CheckInstanceConsole(self, instance, (status, consdata)):
860
    if instance.name == "inst7":
861
      self.assertEqual(status, constants.RS_NORMAL)
862
      console = objects.InstanceConsole.FromDict(consdata)
863
      self.assertTrue(console.Validate())
864
      self.assertEqual(console.host, instance.primary_node)
865
    else:
866
      self.assertEqual(status, constants.RS_UNAVAIL)
867

    
868

    
869
class TestGroupQuery(unittest.TestCase):
870

    
871
  def setUp(self):
872
    self.groups = [
873
      objects.NodeGroup(name="default",
874
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
875
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
876
      objects.NodeGroup(name="restricted",
877
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
878
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
879
      ]
880

    
881
  def _Create(self, selected):
882
    return query.Query(query.GROUP_FIELDS, selected)
883

    
884
  def testSimple(self):
885
    q = self._Create(["name", "uuid", "alloc_policy"])
886
    gqd = query.GroupQueryData(self.groups, None, None)
887

    
888
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
889

    
890
    self.assertEqual(q.Query(gqd),
891
      [[(constants.RS_NORMAL, "default"),
892
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
893
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
894
        ],
895
       [(constants.RS_NORMAL, "restricted"),
896
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
897
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
898
        ],
899
       ])
900

    
901
  def testNodes(self):
902
    groups_to_nodes = {
903
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
904
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
905
      }
906

    
907
    q = self._Create(["name", "node_cnt", "node_list"])
908
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
909

    
910
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
911

    
912
    self.assertEqual(q.Query(gqd),
913
                     [[(constants.RS_NORMAL, "default"),
914
                       (constants.RS_NORMAL, 2),
915
                       (constants.RS_NORMAL, ["node1", "node2"]),
916
                       ],
917
                      [(constants.RS_NORMAL, "restricted"),
918
                       (constants.RS_NORMAL, 3),
919
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
920
                       ],
921
                      ])
922

    
923
  def testInstances(self):
924
    groups_to_instances = {
925
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
926
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
927
      }
928

    
929
    q = self._Create(["pinst_cnt", "pinst_list"])
930
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
931

    
932
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
933

    
934
    self.assertEqual(q.Query(gqd),
935
                     [[(constants.RS_NORMAL, 2),
936
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
937
                       ],
938
                      [(constants.RS_NORMAL, 3),
939
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
940
                       ],
941
                      ])
942

    
943

    
944
class TestOsQuery(unittest.TestCase):
945
  def _Create(self, selected):
946
    return query.Query(query.OS_FIELDS, selected)
947

    
948
  def test(self):
949
    variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
950
    api_versions = [10, 0, 15, 5]
951
    parameters = ["zpar3", "apar9"]
952

    
953
    assert variants != sorted(variants) and variants != utils.NiceSort(variants)
954
    assert (api_versions != sorted(api_versions) and
955
            api_versions != utils.NiceSort(variants))
956
    assert (parameters != sorted(parameters) and
957
            parameters != utils.NiceSort(parameters))
958

    
959
    data = [
960
      query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
961
                   variants=set(), api_versions=set(), parameters=set(),
962
                   node_status={ "some": "status", }),
963
      query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
964
                   variants=set(variants),
965
                   api_versions=set(api_versions),
966
                   parameters=set(parameters),
967
                   node_status={ "some": "other", "status": None, }),
968
      ]
969

    
970

    
971
    q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
972
                      "api_versions", "parameters", "node_status"])
973
    self.assertEqual(q.RequestedData(), set([]))
974
    self.assertEqual(q.Query(data),
975
                     [[(constants.RS_NORMAL, "debian"),
976
                       (constants.RS_NORMAL, False),
977
                       (constants.RS_NORMAL, False),
978
                       (constants.RS_NORMAL, False),
979
                       (constants.RS_NORMAL, []),
980
                       (constants.RS_NORMAL, []),
981
                       (constants.RS_NORMAL, []),
982
                       (constants.RS_NORMAL, {"some": "status"})],
983
                      [(constants.RS_NORMAL, "dos"),
984
                       (constants.RS_NORMAL, True),
985
                       (constants.RS_NORMAL, False),
986
                       (constants.RS_NORMAL, True),
987
                       (constants.RS_NORMAL,
988
                        ["plain", "v00", "v3", "v20", "v33", "var0"]),
989
                       (constants.RS_NORMAL, [0, 5, 10, 15]),
990
                       (constants.RS_NORMAL, ["apar9", "zpar3"]),
991
                       (constants.RS_NORMAL,
992
                        { "some": "other", "status": None, })
993
                       ]])
994

    
995

    
996
class TestQueryFields(unittest.TestCase):
997
  def testAllFields(self):
998
    for fielddefs in query.ALL_FIELD_LISTS:
999
      result = query.QueryFields(fielddefs, None)
1000
      self.assert_(isinstance(result, dict))
1001
      response = objects.QueryFieldsResponse.FromDict(result)
1002
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1003
        [(fdef2.name, fdef2.title)
1004
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1005
                                                key=lambda x: x[0].name)])
1006

    
1007
  def testSomeFields(self):
1008
    rnd = random.Random(5357)
1009

    
1010
    for _ in range(10):
1011
      for fielddefs in query.ALL_FIELD_LISTS:
1012
        if len(fielddefs) > 20:
1013
          sample_size = rnd.randint(5, 20)
1014
        else:
1015
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1016
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1017
                                                         sample_size)]
1018
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1019
        self.assert_(isinstance(result, dict))
1020
        response = objects.QueryFieldsResponse.FromDict(result)
1021
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1022
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
1023

    
1024

    
1025
class TestQueryFilter(unittest.TestCase):
1026
  def testRequestedNames(self):
1027
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1028

    
1029
    for fielddefs in query.ALL_FIELD_LISTS:
1030
      assert "name" in fielddefs
1031

    
1032
      # No name field
1033
      q = query.Query(fielddefs, ["name"], filter_=["=", "name", "abc"],
1034
                      namefield=None)
1035
      self.assertEqual(q.RequestedNames(), None)
1036

    
1037
      # No filter
1038
      q = query.Query(fielddefs, ["name"], filter_=None, namefield="name")
1039
      self.assertEqual(q.RequestedNames(), None)
1040

    
1041
      # Check empty query
1042
      q = query.Query(fielddefs, ["name"], filter_=["|"], namefield="name")
1043
      self.assertEqual(q.RequestedNames(), None)
1044

    
1045
      # Check order
1046
      q = query.Query(fielddefs, ["name"], filter_=["|"] + innerfilter,
1047
                      namefield="name")
1048
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1049

    
1050
      # Check reverse order
1051
      q = query.Query(fielddefs, ["name"],
1052
                      filter_=["|"] + list(reversed(innerfilter)),
1053
                      namefield="name")
1054
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1055

    
1056
      # Duplicates
1057
      q = query.Query(fielddefs, ["name"],
1058
                      filter_=["|"] + innerfilter + list(reversed(innerfilter)),
1059
                      namefield="name")
1060
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1061

    
1062
      # Unknown name field
1063
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1064
                        namefield="_unknown_field_")
1065

    
1066
      # Filter with AND
1067
      q = query.Query(fielddefs, ["name"],
1068
                      filter_=["|", ["=", "name", "foo"],
1069
                                    ["&", ["=", "name", ""]]],
1070
                      namefield="name")
1071
      self.assertTrue(q.RequestedNames() is None)
1072

    
1073
      # Filter with NOT
1074
      q = query.Query(fielddefs, ["name"],
1075
                      filter_=["|", ["=", "name", "foo"],
1076
                                    ["!", ["=", "name", ""]]],
1077
                      namefield="name")
1078
      self.assertTrue(q.RequestedNames() is None)
1079

    
1080
      # Filter with only OR (names must be in correct order)
1081
      q = query.Query(fielddefs, ["name"],
1082
                      filter_=["|", ["=", "name", "x17361"],
1083
                                    ["|", ["=", "name", "x22015"]],
1084
                                    ["|", ["|", ["=", "name", "x13193"]]],
1085
                                    ["=", "name", "x15215"]],
1086
                      namefield="name")
1087
      self.assertEqual(q.RequestedNames(),
1088
                       ["x17361", "x22015", "x13193", "x15215"])
1089

    
1090
  @staticmethod
1091
  def _GenNestedFilter(op, depth):
1092
    nested = ["=", "name", "value"]
1093
    for i in range(depth):
1094
      nested = [op, nested]
1095
    return nested
1096

    
1097
  def testCompileFilter(self):
1098
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1099

    
1100
    checks = [
1101
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1102
      ["=", "_unknown_field", "value"],
1103
      self._GenNestedFilter("|", levels_max),
1104
      self._GenNestedFilter("|", levels_max * 3),
1105
      self._GenNestedFilter("!", levels_max),
1106
      ]
1107

    
1108
    for fielddefs in query.ALL_FIELD_LISTS:
1109
      for filter_ in checks:
1110
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1111
                          fielddefs, None, filter_)
1112

    
1113
      for op in ["|", "!"]:
1114
        filter_ = self._GenNestedFilter(op, levels_max - 1)
1115
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1116
                                                      filter_)))
1117

    
1118
  def testQueryInputOrder(self):
1119
    fielddefs = query._PrepareFieldList([
1120
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1121
       None, 0, lambda ctx, item: item["pnode"]),
1122
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1123
       None, 0, lambda ctx, item: item["snode"]),
1124
      ], [])
1125

    
1126
    data = [
1127
      { "pnode": "node1", "snode": "node44", },
1128
      { "pnode": "node30", "snode": "node90", },
1129
      { "pnode": "node25", "snode": "node1", },
1130
      { "pnode": "node20", "snode": "node1", },
1131
      ]
1132

    
1133
    filter_ = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1134

    
1135
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1136
                    filter_=filter_)
1137
    self.assertTrue(q.RequestedNames() is None)
1138
    self.assertFalse(q.RequestedData())
1139
    self.assertEqual(q.Query(data),
1140
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1141
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1142
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1143

    
1144
    # Try again with reversed input data
1145
    self.assertEqual(q.Query(reversed(data)),
1146
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1147
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1148
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1149

    
1150
    # No name field, result must be in incoming order
1151
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1152
                    filter_=filter_)
1153
    self.assertFalse(q.RequestedData())
1154
    self.assertEqual(q.Query(data),
1155
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1156
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1157
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1158
    self.assertEqual(q.OldStyleQuery(data), [
1159
      ["node1", "node44"],
1160
      ["node25", "node1"],
1161
      ["node20", "node1"],
1162
      ])
1163
    self.assertEqual(q.Query(reversed(data)),
1164
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1165
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1166
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1167
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1168
      ["node20", "node1"],
1169
      ["node25", "node1"],
1170
      ["node1", "node44"],
1171
      ])
1172

    
1173
    # Name field, but no sorting, result must be in incoming order
1174
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1175
    self.assertFalse(q.RequestedData())
1176
    self.assertEqual(q.Query(data, sort_by_name=False),
1177
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1178
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1179
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1180
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1181
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1182
      ["node1", "node44"],
1183
      ["node30", "node90"],
1184
      ["node25", "node1"],
1185
      ["node20", "node1"],
1186
      ])
1187
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1188
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1189
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1190
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1191
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1192
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1193
      ["node20", "node1"],
1194
      ["node25", "node1"],
1195
      ["node30", "node90"],
1196
      ["node1", "node44"],
1197
      ])
1198

    
1199
  def testEqualNamesOrder(self):
1200
    fielddefs = query._PrepareFieldList([
1201
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1202
       None, 0, lambda ctx, item: item["pnode"]),
1203
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1204
       None, 0, lambda ctx, item: item["num"]),
1205
      ], [])
1206

    
1207
    data = [
1208
      { "pnode": "node1", "num": 100, },
1209
      { "pnode": "node1", "num": 25, },
1210
      { "pnode": "node2", "num": 90, },
1211
      { "pnode": "node2", "num": 30, },
1212
      ]
1213

    
1214
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1215
                    filter_=["|", ["=", "pnode", "node1"],
1216
                                  ["=", "pnode", "node2"],
1217
                                  ["=", "pnode", "node1"]])
1218
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1219
                     msg="Did not return unique names")
1220
    self.assertFalse(q.RequestedData())
1221
    self.assertEqual(q.Query(data),
1222
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1223
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1224
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1225
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1226
    self.assertEqual(q.Query(data, sort_by_name=False),
1227
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1228
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1229
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1230
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1231

    
1232
    data = [
1233
      { "pnode": "nodeX", "num": 50, },
1234
      { "pnode": "nodeY", "num": 40, },
1235
      { "pnode": "nodeX", "num": 30, },
1236
      { "pnode": "nodeX", "num": 20, },
1237
      { "pnode": "nodeM", "num": 10, },
1238
      ]
1239

    
1240
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1241
                    filter_=["|", ["=", "pnode", "nodeX"],
1242
                                  ["=", "pnode", "nodeY"],
1243
                                  ["=", "pnode", "nodeY"],
1244
                                  ["=", "pnode", "nodeY"],
1245
                                  ["=", "pnode", "nodeM"]])
1246
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1247
                     msg="Did not return unique names")
1248
    self.assertFalse(q.RequestedData())
1249

    
1250
    # First sorted by name, then input order
1251
    self.assertEqual(q.Query(data, sort_by_name=True),
1252
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1253
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1254
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1255
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1256
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1257

    
1258
    # Input order
1259
    self.assertEqual(q.Query(data, sort_by_name=False),
1260
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1261
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1262
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1263
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1264
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1265

    
1266
  def testFilter(self):
1267
    (DK_A, DK_B) = range(1000, 1002)
1268

    
1269
    fielddefs = query._PrepareFieldList([
1270
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1271
       DK_A, 0, lambda ctx, item: item["name"]),
1272
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1273
       DK_B, 0, lambda ctx, item: item["other"]),
1274
      ], [])
1275

    
1276
    data = [
1277
      { "name": "node1", "other": "foo", },
1278
      { "name": "node2", "other": "bar", },
1279
      { "name": "node3", "other": "Hello", },
1280
      ]
1281

    
1282
    # Empty filter
1283
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1284
                    filter_=["|"])
1285
    self.assertTrue(q.RequestedNames() is None)
1286
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1287
    self.assertEqual(q.Query(data), [])
1288

    
1289
    # Normal filter
1290
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1291
                    filter_=["=", "name", "node1"])
1292
    self.assertEqual(q.RequestedNames(), ["node1"])
1293
    self.assertEqual(q.Query(data),
1294
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1295

    
1296
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1297
                    filter_=(["|", ["=", "name", "node1"],
1298
                                   ["=", "name", "node3"]]))
1299
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1300
    self.assertEqual(q.Query(data),
1301
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1302
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1303

    
1304
    # Complex filter
1305
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1306
                    filter_=(["|", ["=", "name", "node1"],
1307
                                   ["|", ["=", "name", "node3"],
1308
                                         ["=", "name", "node2"]],
1309
                                   ["=", "name", "node3"]]))
1310
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1311
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1312
    self.assertEqual(q.Query(data),
1313
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1314
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1315
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1316

    
1317
    # Filter data type mismatch
1318
    for i in [-1, 0, 1, 123, [], None, True, False]:
1319
      self.assertRaises(errors.ParameterError, query.Query,
1320
                        fielddefs, ["name", "other"], namefield="name",
1321
                        filter_=["=", "name", i])
1322

    
1323
    # Negative filter
1324
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1325
                    filter_=["!", ["|", ["=", "name", "node1"],
1326
                                        ["=", "name", "node3"]]])
1327
    self.assertTrue(q.RequestedNames() is None)
1328
    self.assertEqual(q.Query(data),
1329
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1330

    
1331
    # Not equal
1332
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1333
                    filter_=["!=", "name", "node3"])
1334
    self.assertTrue(q.RequestedNames() is None)
1335
    self.assertEqual(q.Query(data),
1336
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1337
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1338

    
1339
    # Data type
1340
    q = query.Query(fielddefs, [], namefield="name",
1341
                    filter_=["|", ["=", "other", "bar"],
1342
                                  ["=", "name", "foo"]])
1343
    self.assertTrue(q.RequestedNames() is None)
1344
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1345
    self.assertEqual(q.Query(data), [[]])
1346

    
1347
    # Only one data type
1348
    q = query.Query(fielddefs, ["other"], namefield="name",
1349
                    filter_=["=", "other", "bar"])
1350
    self.assertTrue(q.RequestedNames() is None)
1351
    self.assertEqual(q.RequestedData(), set([DK_B]))
1352
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1353

    
1354
    q = query.Query(fielddefs, [], namefield="name",
1355
                    filter_=["=", "other", "bar"])
1356
    self.assertTrue(q.RequestedNames() is None)
1357
    self.assertEqual(q.RequestedData(), set([DK_B]))
1358
    self.assertEqual(q.Query(data), [[]])
1359

    
1360
  def testFilterContains(self):
1361
    fielddefs = query._PrepareFieldList([
1362
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1363
       None, 0, lambda ctx, item: item["name"]),
1364
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1365
       None, 0, lambda ctx, item: item["other"]),
1366
      ], [])
1367

    
1368
    data = [
1369
      { "name": "node2", "other": ["x", "y", "bar"], },
1370
      { "name": "node3", "other": "Hello", },
1371
      { "name": "node1", "other": ["a", "b", "foo"], },
1372
      { "name": "empty", "other": []},
1373
      ]
1374

    
1375
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1376
                    filter_=["=[]", "other", "bar"])
1377
    self.assertTrue(q.RequestedNames() is None)
1378
    self.assertEqual(q.Query(data), [
1379
      [(constants.RS_NORMAL, "node2"),
1380
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1381
      ])
1382

    
1383
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1384
                    filter_=["|", ["=[]", "other", "bar"],
1385
                                  ["=[]", "other", "a"],
1386
                                  ["=[]", "other", "b"]])
1387
    self.assertTrue(q.RequestedNames() is None)
1388
    self.assertEqual(q.Query(data), [
1389
      [(constants.RS_NORMAL, "node1"),
1390
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1391
      [(constants.RS_NORMAL, "node2"),
1392
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1393
      ])
1394
    self.assertEqual(q.OldStyleQuery(data), [
1395
      ["node1", ["a", "b", "foo"]],
1396
      ["node2", ["x", "y", "bar"]],
1397
      ])
1398

    
1399
    # Boolean test
1400
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1401
                    filter_=["?", "other"])
1402
    self.assertEqual(q.OldStyleQuery(data), [
1403
      ["node1", ["a", "b", "foo"]],
1404
      ["node2", ["x", "y", "bar"]],
1405
      ["node3", "Hello"],
1406
      ])
1407

    
1408
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1409
                    filter_=["!", ["?", "other"]])
1410
    self.assertEqual(q.OldStyleQuery(data), [
1411
      ["empty", []],
1412
      ])
1413

    
1414
  def testFilterHostname(self):
1415
    fielddefs = query._PrepareFieldList([
1416
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1417
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1418
      ], [])
1419

    
1420
    data = [
1421
      { "name": "node1.example.com", },
1422
      { "name": "node2.example.com", },
1423
      { "name": "node2.example.net", },
1424
      ]
1425

    
1426
    q = query.Query(fielddefs, ["name"], namefield="name",
1427
                    filter_=["=", "name", "node2"])
1428
    self.assertEqual(q.RequestedNames(), ["node2"])
1429
    self.assertEqual(q.Query(data), [
1430
      [(constants.RS_NORMAL, "node2.example.com")],
1431
      [(constants.RS_NORMAL, "node2.example.net")],
1432
      ])
1433

    
1434
    q = query.Query(fielddefs, ["name"], namefield="name",
1435
                    filter_=["=", "name", "node1"])
1436
    self.assertEqual(q.RequestedNames(), ["node1"])
1437
    self.assertEqual(q.Query(data), [
1438
      [(constants.RS_NORMAL, "node1.example.com")],
1439
      ])
1440

    
1441
    q = query.Query(fielddefs, ["name"], namefield="name",
1442
                    filter_=["=", "name", "othername"])
1443
    self.assertEqual(q.RequestedNames(), ["othername"])
1444
    self.assertEqual(q.Query(data), [])
1445

    
1446
    q = query.Query(fielddefs, ["name"], namefield="name",
1447
                    filter_=["|", ["=", "name", "node1.example.com"],
1448
                                  ["=", "name", "node2"]])
1449
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1450
    self.assertEqual(q.Query(data), [
1451
      [(constants.RS_NORMAL, "node1.example.com")],
1452
      [(constants.RS_NORMAL, "node2.example.com")],
1453
      [(constants.RS_NORMAL, "node2.example.net")],
1454
      ])
1455
    self.assertEqual(q.OldStyleQuery(data), [
1456
      ["node1.example.com"],
1457
      ["node2.example.com"],
1458
      ["node2.example.net"],
1459
      ])
1460

    
1461
    q = query.Query(fielddefs, ["name"], namefield="name",
1462
                    filter_=["!=", "name", "node1"])
1463
    self.assertTrue(q.RequestedNames() is None)
1464
    self.assertEqual(q.Query(data), [
1465
      [(constants.RS_NORMAL, "node2.example.com")],
1466
      [(constants.RS_NORMAL, "node2.example.net")],
1467
      ])
1468
    self.assertEqual(q.OldStyleQuery(data), [
1469
      ["node2.example.com"],
1470
      ["node2.example.net"],
1471
      ])
1472

    
1473
  def testFilterBoolean(self):
1474
    fielddefs = query._PrepareFieldList([
1475
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1476
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1477
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1478
       None, 0, lambda ctx, item: item["value"]),
1479
      ], [])
1480

    
1481
    data = [
1482
      { "name": "node1", "value": False, },
1483
      { "name": "node2", "value": True, },
1484
      { "name": "node3", "value": True, },
1485
      ]
1486

    
1487
    q = query.Query(fielddefs, ["name", "value"],
1488
                    filter_=["|", ["=", "value", False],
1489
                                  ["=", "value", True]])
1490
    self.assertTrue(q.RequestedNames() is None)
1491
    self.assertEqual(q.Query(data), [
1492
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1493
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1494
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1495
      ])
1496

    
1497
    q = query.Query(fielddefs, ["name", "value"],
1498
                    filter_=["|", ["=", "value", False],
1499
                                  ["!", ["=", "value", False]]])
1500
    self.assertTrue(q.RequestedNames() is None)
1501
    self.assertEqual(q.Query(data), [
1502
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1503
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1504
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1505
      ])
1506

    
1507
    # Comparing bool with string
1508
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1509
      self.assertRaises(errors.ParameterError, query.Query,
1510
                        fielddefs, ["name", "value"],
1511
                        filter_=["=", "value", i])
1512

    
1513
    # Truth filter
1514
    q = query.Query(fielddefs, ["name", "value"], filter_=["?", "value"])
1515
    self.assertTrue(q.RequestedNames() is None)
1516
    self.assertEqual(q.Query(data), [
1517
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1518
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1519
      ])
1520

    
1521
    # Negative bool filter
1522
    q = query.Query(fielddefs, ["name", "value"], filter_=["!", ["?", "value"]])
1523
    self.assertTrue(q.RequestedNames() is None)
1524
    self.assertEqual(q.Query(data), [
1525
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1526
      ])
1527

    
1528
    # Complex truth filter
1529
    q = query.Query(fielddefs, ["name", "value"],
1530
                    filter_=["|", ["&", ["=", "name", "node1"],
1531
                                        ["!", ["?", "value"]]],
1532
                                  ["?", "value"]])
1533
    self.assertTrue(q.RequestedNames() is None)
1534
    self.assertEqual(q.Query(data), [
1535
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1536
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1537
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1538
      ])
1539

    
1540

    
1541
if __name__ == "__main__":
1542
  testutils.GanetiTestProgram()