Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 3b877f08

History | View | Annotate | Download (58 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 TestQueryFields(unittest.TestCase):
945
  def testAllFields(self):
946
    for fielddefs in query.ALL_FIELD_LISTS:
947
      result = query.QueryFields(fielddefs, None)
948
      self.assert_(isinstance(result, dict))
949
      response = objects.QueryFieldsResponse.FromDict(result)
950
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
951
        [(fdef2.name, fdef2.title)
952
         for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
953
                                                key=lambda x: x[0].name)])
954

    
955
  def testSomeFields(self):
956
    rnd = random.Random(5357)
957

    
958
    for _ in range(10):
959
      for fielddefs in query.ALL_FIELD_LISTS:
960
        if len(fielddefs) > 20:
961
          sample_size = rnd.randint(5, 20)
962
        else:
963
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
964
        fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
965
                                                         sample_size)]
966
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
967
        self.assert_(isinstance(result, dict))
968
        response = objects.QueryFieldsResponse.FromDict(result)
969
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
970
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
971

    
972

    
973
class TestQueryFilter(unittest.TestCase):
974
  def testRequestedNames(self):
975
    innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
976

    
977
    for fielddefs in query.ALL_FIELD_LISTS:
978
      assert "name" in fielddefs
979

    
980
      # No name field
981
      q = query.Query(fielddefs, ["name"], filter_=["=", "name", "abc"],
982
                      namefield=None)
983
      self.assertEqual(q.RequestedNames(), None)
984

    
985
      # No filter
986
      q = query.Query(fielddefs, ["name"], filter_=None, namefield="name")
987
      self.assertEqual(q.RequestedNames(), None)
988

    
989
      # Check empty query
990
      q = query.Query(fielddefs, ["name"], filter_=["|"], namefield="name")
991
      self.assertEqual(q.RequestedNames(), None)
992

    
993
      # Check order
994
      q = query.Query(fielddefs, ["name"], filter_=["|"] + innerfilter,
995
                      namefield="name")
996
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
997

    
998
      # Check reverse order
999
      q = query.Query(fielddefs, ["name"],
1000
                      filter_=["|"] + list(reversed(innerfilter)),
1001
                      namefield="name")
1002
      self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1003

    
1004
      # Duplicates
1005
      q = query.Query(fielddefs, ["name"],
1006
                      filter_=["|"] + innerfilter + list(reversed(innerfilter)),
1007
                      namefield="name")
1008
      self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1009

    
1010
      # Unknown name field
1011
      self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1012
                        namefield="_unknown_field_")
1013

    
1014
      # Filter with AND
1015
      q = query.Query(fielddefs, ["name"],
1016
                      filter_=["|", ["=", "name", "foo"],
1017
                                    ["&", ["=", "name", ""]]],
1018
                      namefield="name")
1019
      self.assertTrue(q.RequestedNames() is None)
1020

    
1021
      # Filter with NOT
1022
      q = query.Query(fielddefs, ["name"],
1023
                      filter_=["|", ["=", "name", "foo"],
1024
                                    ["!", ["=", "name", ""]]],
1025
                      namefield="name")
1026
      self.assertTrue(q.RequestedNames() is None)
1027

    
1028
      # Filter with only OR (names must be in correct order)
1029
      q = query.Query(fielddefs, ["name"],
1030
                      filter_=["|", ["=", "name", "x17361"],
1031
                                    ["|", ["=", "name", "x22015"]],
1032
                                    ["|", ["|", ["=", "name", "x13193"]]],
1033
                                    ["=", "name", "x15215"]],
1034
                      namefield="name")
1035
      self.assertEqual(q.RequestedNames(),
1036
                       ["x17361", "x22015", "x13193", "x15215"])
1037

    
1038
  @staticmethod
1039
  def _GenNestedFilter(op, depth):
1040
    nested = ["=", "name", "value"]
1041
    for i in range(depth):
1042
      nested = [op, nested]
1043
    return nested
1044

    
1045
  def testCompileFilter(self):
1046
    levels_max = query._FilterCompilerHelper._LEVELS_MAX
1047

    
1048
    checks = [
1049
      [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1050
      ["=", "_unknown_field", "value"],
1051
      self._GenNestedFilter("|", levels_max),
1052
      self._GenNestedFilter("|", levels_max * 3),
1053
      self._GenNestedFilter("!", levels_max),
1054
      ]
1055

    
1056
    for fielddefs in query.ALL_FIELD_LISTS:
1057
      for filter_ in checks:
1058
        self.assertRaises(errors.ParameterError, query._CompileFilter,
1059
                          fielddefs, None, filter_)
1060

    
1061
      for op in ["|", "!"]:
1062
        filter_ = self._GenNestedFilter(op, levels_max - 1)
1063
        self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1064
                                                      filter_)))
1065

    
1066
  def testQueryInputOrder(self):
1067
    fielddefs = query._PrepareFieldList([
1068
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1069
       None, 0, lambda ctx, item: item["pnode"]),
1070
      (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1071
       None, 0, lambda ctx, item: item["snode"]),
1072
      ], [])
1073

    
1074
    data = [
1075
      { "pnode": "node1", "snode": "node44", },
1076
      { "pnode": "node30", "snode": "node90", },
1077
      { "pnode": "node25", "snode": "node1", },
1078
      { "pnode": "node20", "snode": "node1", },
1079
      ]
1080

    
1081
    filter_ = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1082

    
1083
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1084
                    filter_=filter_)
1085
    self.assertTrue(q.RequestedNames() is None)
1086
    self.assertFalse(q.RequestedData())
1087
    self.assertEqual(q.Query(data),
1088
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1089
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1090
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1091

    
1092
    # Try again with reversed input data
1093
    self.assertEqual(q.Query(reversed(data)),
1094
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1095
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1096
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1097

    
1098
    # No name field, result must be in incoming order
1099
    q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1100
                    filter_=filter_)
1101
    self.assertFalse(q.RequestedData())
1102
    self.assertEqual(q.Query(data),
1103
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1104
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1105
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1106
    self.assertEqual(q.OldStyleQuery(data), [
1107
      ["node1", "node44"],
1108
      ["node25", "node1"],
1109
      ["node20", "node1"],
1110
      ])
1111
    self.assertEqual(q.Query(reversed(data)),
1112
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1113
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1114
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1115
    self.assertEqual(q.OldStyleQuery(reversed(data)), [
1116
      ["node20", "node1"],
1117
      ["node25", "node1"],
1118
      ["node1", "node44"],
1119
      ])
1120

    
1121
    # Name field, but no sorting, result must be in incoming order
1122
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1123
    self.assertFalse(q.RequestedData())
1124
    self.assertEqual(q.Query(data, sort_by_name=False),
1125
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1126
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1127
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1128
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1129
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1130
      ["node1", "node44"],
1131
      ["node30", "node90"],
1132
      ["node25", "node1"],
1133
      ["node20", "node1"],
1134
      ])
1135
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1136
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1137
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1138
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1139
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1140
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1141
      ["node20", "node1"],
1142
      ["node25", "node1"],
1143
      ["node30", "node90"],
1144
      ["node1", "node44"],
1145
      ])
1146

    
1147
  def testEqualNamesOrder(self):
1148
    fielddefs = query._PrepareFieldList([
1149
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1150
       None, 0, lambda ctx, item: item["pnode"]),
1151
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1152
       None, 0, lambda ctx, item: item["num"]),
1153
      ], [])
1154

    
1155
    data = [
1156
      { "pnode": "node1", "num": 100, },
1157
      { "pnode": "node1", "num": 25, },
1158
      { "pnode": "node2", "num": 90, },
1159
      { "pnode": "node2", "num": 30, },
1160
      ]
1161

    
1162
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1163
                    filter_=["|", ["=", "pnode", "node1"],
1164
                                  ["=", "pnode", "node2"],
1165
                                  ["=", "pnode", "node1"]])
1166
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1167
                     msg="Did not return unique names")
1168
    self.assertFalse(q.RequestedData())
1169
    self.assertEqual(q.Query(data),
1170
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1171
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1172
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1173
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1174
    self.assertEqual(q.Query(data, sort_by_name=False),
1175
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1176
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1177
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1178
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1179

    
1180
    data = [
1181
      { "pnode": "nodeX", "num": 50, },
1182
      { "pnode": "nodeY", "num": 40, },
1183
      { "pnode": "nodeX", "num": 30, },
1184
      { "pnode": "nodeX", "num": 20, },
1185
      { "pnode": "nodeM", "num": 10, },
1186
      ]
1187

    
1188
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1189
                    filter_=["|", ["=", "pnode", "nodeX"],
1190
                                  ["=", "pnode", "nodeY"],
1191
                                  ["=", "pnode", "nodeY"],
1192
                                  ["=", "pnode", "nodeY"],
1193
                                  ["=", "pnode", "nodeM"]])
1194
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1195
                     msg="Did not return unique names")
1196
    self.assertFalse(q.RequestedData())
1197

    
1198
    # First sorted by name, then input order
1199
    self.assertEqual(q.Query(data, sort_by_name=True),
1200
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1201
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1202
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1203
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1204
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1205

    
1206
    # Input order
1207
    self.assertEqual(q.Query(data, sort_by_name=False),
1208
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1209
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1210
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1211
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1212
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1213

    
1214
  def testFilter(self):
1215
    (DK_A, DK_B) = range(1000, 1002)
1216

    
1217
    fielddefs = query._PrepareFieldList([
1218
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1219
       DK_A, 0, lambda ctx, item: item["name"]),
1220
      (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1221
       DK_B, 0, lambda ctx, item: item["other"]),
1222
      ], [])
1223

    
1224
    data = [
1225
      { "name": "node1", "other": "foo", },
1226
      { "name": "node2", "other": "bar", },
1227
      { "name": "node3", "other": "Hello", },
1228
      ]
1229

    
1230
    # Empty filter
1231
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1232
                    filter_=["|"])
1233
    self.assertTrue(q.RequestedNames() is None)
1234
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1235
    self.assertEqual(q.Query(data), [])
1236

    
1237
    # Normal filter
1238
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1239
                    filter_=["=", "name", "node1"])
1240
    self.assertEqual(q.RequestedNames(), ["node1"])
1241
    self.assertEqual(q.Query(data),
1242
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1243

    
1244
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1245
                    filter_=(["|", ["=", "name", "node1"],
1246
                                   ["=", "name", "node3"]]))
1247
    self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1248
    self.assertEqual(q.Query(data),
1249
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1250
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1251

    
1252
    # Complex filter
1253
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1254
                    filter_=(["|", ["=", "name", "node1"],
1255
                                   ["|", ["=", "name", "node3"],
1256
                                         ["=", "name", "node2"]],
1257
                                   ["=", "name", "node3"]]))
1258
    self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1259
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1260
    self.assertEqual(q.Query(data),
1261
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1262
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1263
       [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1264

    
1265
    # Filter data type mismatch
1266
    for i in [-1, 0, 1, 123, [], None, True, False]:
1267
      self.assertRaises(errors.ParameterError, query.Query,
1268
                        fielddefs, ["name", "other"], namefield="name",
1269
                        filter_=["=", "name", i])
1270

    
1271
    # Negative filter
1272
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1273
                    filter_=["!", ["|", ["=", "name", "node1"],
1274
                                        ["=", "name", "node3"]]])
1275
    self.assertTrue(q.RequestedNames() is None)
1276
    self.assertEqual(q.Query(data),
1277
      [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1278

    
1279
    # Not equal
1280
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1281
                    filter_=["!=", "name", "node3"])
1282
    self.assertTrue(q.RequestedNames() is None)
1283
    self.assertEqual(q.Query(data),
1284
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1285
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1286

    
1287
    # Data type
1288
    q = query.Query(fielddefs, [], namefield="name",
1289
                    filter_=["|", ["=", "other", "bar"],
1290
                                  ["=", "name", "foo"]])
1291
    self.assertTrue(q.RequestedNames() is None)
1292
    self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1293
    self.assertEqual(q.Query(data), [[]])
1294

    
1295
    # Only one data type
1296
    q = query.Query(fielddefs, ["other"], namefield="name",
1297
                    filter_=["=", "other", "bar"])
1298
    self.assertTrue(q.RequestedNames() is None)
1299
    self.assertEqual(q.RequestedData(), set([DK_B]))
1300
    self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1301

    
1302
    q = query.Query(fielddefs, [], namefield="name",
1303
                    filter_=["=", "other", "bar"])
1304
    self.assertTrue(q.RequestedNames() is None)
1305
    self.assertEqual(q.RequestedData(), set([DK_B]))
1306
    self.assertEqual(q.Query(data), [[]])
1307

    
1308
  def testFilterContains(self):
1309
    fielddefs = query._PrepareFieldList([
1310
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1311
       None, 0, lambda ctx, item: item["name"]),
1312
      (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1313
       None, 0, lambda ctx, item: item["other"]),
1314
      ], [])
1315

    
1316
    data = [
1317
      { "name": "node2", "other": ["x", "y", "bar"], },
1318
      { "name": "node3", "other": "Hello", },
1319
      { "name": "node1", "other": ["a", "b", "foo"], },
1320
      { "name": "empty", "other": []},
1321
      ]
1322

    
1323
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1324
                    filter_=["=[]", "other", "bar"])
1325
    self.assertTrue(q.RequestedNames() is None)
1326
    self.assertEqual(q.Query(data), [
1327
      [(constants.RS_NORMAL, "node2"),
1328
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1329
      ])
1330

    
1331
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1332
                    filter_=["|", ["=[]", "other", "bar"],
1333
                                  ["=[]", "other", "a"],
1334
                                  ["=[]", "other", "b"]])
1335
    self.assertTrue(q.RequestedNames() is None)
1336
    self.assertEqual(q.Query(data), [
1337
      [(constants.RS_NORMAL, "node1"),
1338
       (constants.RS_NORMAL, ["a", "b", "foo"])],
1339
      [(constants.RS_NORMAL, "node2"),
1340
       (constants.RS_NORMAL, ["x", "y", "bar"])],
1341
      ])
1342
    self.assertEqual(q.OldStyleQuery(data), [
1343
      ["node1", ["a", "b", "foo"]],
1344
      ["node2", ["x", "y", "bar"]],
1345
      ])
1346

    
1347
    # Boolean test
1348
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1349
                    filter_=["?", "other"])
1350
    self.assertEqual(q.OldStyleQuery(data), [
1351
      ["node1", ["a", "b", "foo"]],
1352
      ["node2", ["x", "y", "bar"]],
1353
      ["node3", "Hello"],
1354
      ])
1355

    
1356
    q = query.Query(fielddefs, ["name", "other"], namefield="name",
1357
                    filter_=["!", ["?", "other"]])
1358
    self.assertEqual(q.OldStyleQuery(data), [
1359
      ["empty", []],
1360
      ])
1361

    
1362
  def testFilterHostname(self):
1363
    fielddefs = query._PrepareFieldList([
1364
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1365
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1366
      ], [])
1367

    
1368
    data = [
1369
      { "name": "node1.example.com", },
1370
      { "name": "node2.example.com", },
1371
      { "name": "node2.example.net", },
1372
      ]
1373

    
1374
    q = query.Query(fielddefs, ["name"], namefield="name",
1375
                    filter_=["=", "name", "node2"])
1376
    self.assertEqual(q.RequestedNames(), ["node2"])
1377
    self.assertEqual(q.Query(data), [
1378
      [(constants.RS_NORMAL, "node2.example.com")],
1379
      [(constants.RS_NORMAL, "node2.example.net")],
1380
      ])
1381

    
1382
    q = query.Query(fielddefs, ["name"], namefield="name",
1383
                    filter_=["=", "name", "node1"])
1384
    self.assertEqual(q.RequestedNames(), ["node1"])
1385
    self.assertEqual(q.Query(data), [
1386
      [(constants.RS_NORMAL, "node1.example.com")],
1387
      ])
1388

    
1389
    q = query.Query(fielddefs, ["name"], namefield="name",
1390
                    filter_=["=", "name", "othername"])
1391
    self.assertEqual(q.RequestedNames(), ["othername"])
1392
    self.assertEqual(q.Query(data), [])
1393

    
1394
    q = query.Query(fielddefs, ["name"], namefield="name",
1395
                    filter_=["|", ["=", "name", "node1.example.com"],
1396
                                  ["=", "name", "node2"]])
1397
    self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1398
    self.assertEqual(q.Query(data), [
1399
      [(constants.RS_NORMAL, "node1.example.com")],
1400
      [(constants.RS_NORMAL, "node2.example.com")],
1401
      [(constants.RS_NORMAL, "node2.example.net")],
1402
      ])
1403
    self.assertEqual(q.OldStyleQuery(data), [
1404
      ["node1.example.com"],
1405
      ["node2.example.com"],
1406
      ["node2.example.net"],
1407
      ])
1408

    
1409
    q = query.Query(fielddefs, ["name"], namefield="name",
1410
                    filter_=["!=", "name", "node1"])
1411
    self.assertTrue(q.RequestedNames() is None)
1412
    self.assertEqual(q.Query(data), [
1413
      [(constants.RS_NORMAL, "node2.example.com")],
1414
      [(constants.RS_NORMAL, "node2.example.net")],
1415
      ])
1416
    self.assertEqual(q.OldStyleQuery(data), [
1417
      ["node2.example.com"],
1418
      ["node2.example.net"],
1419
      ])
1420

    
1421
  def testFilterBoolean(self):
1422
    fielddefs = query._PrepareFieldList([
1423
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1424
       None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1425
      (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1426
       None, 0, lambda ctx, item: item["value"]),
1427
      ], [])
1428

    
1429
    data = [
1430
      { "name": "node1", "value": False, },
1431
      { "name": "node2", "value": True, },
1432
      { "name": "node3", "value": True, },
1433
      ]
1434

    
1435
    q = query.Query(fielddefs, ["name", "value"],
1436
                    filter_=["|", ["=", "value", False],
1437
                                  ["=", "value", True]])
1438
    self.assertTrue(q.RequestedNames() is None)
1439
    self.assertEqual(q.Query(data), [
1440
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1441
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1442
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1443
      ])
1444

    
1445
    q = query.Query(fielddefs, ["name", "value"],
1446
                    filter_=["|", ["=", "value", False],
1447
                                  ["!", ["=", "value", False]]])
1448
    self.assertTrue(q.RequestedNames() is None)
1449
    self.assertEqual(q.Query(data), [
1450
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1451
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1452
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1453
      ])
1454

    
1455
    # Comparing bool with string
1456
    for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1457
      self.assertRaises(errors.ParameterError, query.Query,
1458
                        fielddefs, ["name", "value"],
1459
                        filter_=["=", "value", i])
1460

    
1461
    # Truth filter
1462
    q = query.Query(fielddefs, ["name", "value"], filter_=["?", "value"])
1463
    self.assertTrue(q.RequestedNames() is None)
1464
    self.assertEqual(q.Query(data), [
1465
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1466
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1467
      ])
1468

    
1469
    # Negative bool filter
1470
    q = query.Query(fielddefs, ["name", "value"], filter_=["!", ["?", "value"]])
1471
    self.assertTrue(q.RequestedNames() is None)
1472
    self.assertEqual(q.Query(data), [
1473
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1474
      ])
1475

    
1476
    # Complex truth filter
1477
    q = query.Query(fielddefs, ["name", "value"],
1478
                    filter_=["|", ["&", ["=", "name", "node1"],
1479
                                        ["!", ["?", "value"]]],
1480
                                  ["?", "value"]])
1481
    self.assertTrue(q.RequestedNames() is None)
1482
    self.assertEqual(q.Query(data), [
1483
      [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1484
      [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1485
      [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1486
      ])
1487

    
1488

    
1489
if __name__ == "__main__":
1490
  testutils.GanetiTestProgram()