Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 8572f1fe

History | View | Annotate | Download (33.3 kB)

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

    
4
# Copyright (C) 2010 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 (constants.QRFS_NORMAL, disks[nr])
60
  except IndexError:
61
    return (constants.QRFS_UNAVAIL, None)
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),
70
       STATIC, lambda ctx, item: (constants.QRFS_NORMAL, item["name"])),
71
      (query._MakeField("master", "Master", constants.QFT_BOOL),
72
       STATIC, lambda ctx, item: (constants.QRFS_NORMAL,
73
                                  ctx.mastername == item["name"])),
74
      ] +
75
      [(query._MakeField("disk%s.size" % i, "DiskSize%s" % i,
76
                         constants.QFT_UNIT),
77
        DISK, compat.partial(_GetDiskSize, i))
78
       for i in range(4)])
79

    
80
    q = query.Query(fielddef, ["name"])
81
    self.assertEqual(q.RequestedData(), set([STATIC]))
82
    self.assertEqual(len(q._fields), 1)
83
    self.assertEqual(len(q.GetFields()), 1)
84
    self.assertEqual(q.GetFields()[0].ToDict(),
85
      objects.QueryFieldDefinition(name="name",
86
                                   title="Name",
87
                                   kind=constants.QFT_TEXT).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.QRFS_NORMAL, "node1")],
98
                      [(constants.QRFS_NORMAL, "node2")],
99
                      [(constants.QRFS_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.QRFS_NORMAL, "node1"),
108
                       (constants.QRFS_NORMAL, False)],
109
                      [(constants.QRFS_NORMAL, "node2"),
110
                       (constants.QRFS_NORMAL, False)],
111
                      [(constants.QRFS_NORMAL, "node3"),
112
                       (constants.QRFS_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.QRFS_NORMAL, "node1"),
120
                       (constants.QRFS_NORMAL, False),
121
                       (constants.QRFS_NORMAL, 0)],
122
                      [(constants.QRFS_NORMAL, "node2"),
123
                       (constants.QRFS_NORMAL, True),
124
                       (constants.QRFS_NORMAL, 3)],
125
                      [(constants.QRFS_NORMAL, "node3"),
126
                       (constants.QRFS_NORMAL, False),
127
                       (constants.QRFS_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.QRFS_NORMAL, 2),
137
                       (constants.QRFS_NORMAL, 1),
138
                       (constants.QRFS_UNKNOWN, None),
139
                       (constants.QRFS_NORMAL, 0)],
140
                      [(constants.QRFS_UNAVAIL, None),
141
                       (constants.QRFS_NORMAL, 4),
142
                       (constants.QRFS_UNKNOWN, None),
143
                       (constants.QRFS_NORMAL, 3)],
144
                      [(constants.QRFS_NORMAL, 7),
145
                       (constants.QRFS_NORMAL, 6),
146
                       (constants.QRFS_UNKNOWN, None),
147
                       (constants.QRFS_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, },
154
                     { "name": "disk1.size", "title": "DiskSize1",
155
                       "kind": constants.QFT_UNIT, },
156
                     { "name": "disk99.size", "title": "disk99.size",
157
                       "kind": constants.QFT_UNKNOWN, },
158
                     { "name": "disk0.size", "title": "DiskSize0",
159
                       "kind": constants.QFT_UNIT, },
160
                     ])
161

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

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

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

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

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

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

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

    
215
  def testUnknown(self):
216
    fielddef = query._PrepareFieldList([
217
      (query._MakeField("name", "Name", constants.QFT_TEXT),
218
       None, lambda _, item: (constants.QRFS_NORMAL, "name%s" % item)),
219
      (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP),
220
       None, lambda *args: (constants.QRFS_NORMAL, 1234)),
221
      (query._MakeField("nodata", "NoData", constants.QFT_NUMBER),
222
       None, lambda *args: (constants.QRFS_NODATA, None)),
223
      (query._MakeField("unavail", "Unavail", constants.QFT_BOOL),
224
       None, lambda *args: (constants.QRFS_UNAVAIL, None)),
225
      ])
226

    
227
    for selected in [["foo"], ["Hello", "World"],
228
                     ["name1", "other", "foo"]]:
229
      q = query.Query(fielddef, selected)
230
      self.assertEqual(len(q._fields), len(selected))
231
      self.assert_(compat.all(len(row) == len(selected)
232
                              for row in q.Query(_QueryData(range(1, 10)))))
233
      self.assertEqual(q.Query(_QueryData(range(1, 10))),
234
                       [[(constants.QRFS_UNKNOWN, None)] * len(selected)
235
                        for i in range(1, 10)])
236
      self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
237
                       [{ "name": name, "title": name,
238
                          "kind": constants.QFT_UNKNOWN, }
239
                        for name in selected])
240

    
241
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
242
    self.assertEqual(len(q._fields), 4)
243
    self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
244
                     ["name%s" % i, 1234, None, None]
245
                     for i in range(1, 10)
246
                     ])
247

    
248
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
249
    self.assertEqual(len(q._fields), 5)
250
    self.assertEqual(q.Query(_QueryData(range(1, 10))),
251
                     [[(constants.QRFS_NORMAL, "name%s" % i),
252
                       (constants.QRFS_NORMAL, 1234),
253
                       (constants.QRFS_NODATA, None),
254
                       (constants.QRFS_UNAVAIL, None),
255
                       (constants.QRFS_UNKNOWN, None)]
256
                      for i in range(1, 10)])
257

    
258

    
259
class TestGetNodeRole(unittest.TestCase):
260
  def testMaster(self):
261
    node = objects.Node(name="node1")
262
    self.assertEqual(query._GetNodeRole(node, "node1"), "M")
263

    
264
  def testMasterCandidate(self):
265
    node = objects.Node(name="node1", master_candidate=True)
266
    self.assertEqual(query._GetNodeRole(node, "master"), "C")
267

    
268
  def testRegular(self):
269
    node = objects.Node(name="node1")
270
    self.assertEqual(query._GetNodeRole(node, "master"), "R")
271

    
272
  def testDrained(self):
273
    node = objects.Node(name="node1", drained=True)
274
    self.assertEqual(query._GetNodeRole(node, "master"), "D")
275

    
276
  def testOffline(self):
277
    node = objects.Node(name="node1", offline=True)
278
    self.assertEqual(query._GetNodeRole(node, "master"), "O")
279

    
280

    
281
class TestNodeQuery(unittest.TestCase):
282
  def _Create(self, selected):
283
    return query.Query(query.NODE_FIELDS, selected)
284

    
285
  def testSimple(self):
286
    nodes = [
287
      objects.Node(name="node1", drained=False),
288
      objects.Node(name="node2", drained=True),
289
      objects.Node(name="node3", drained=False),
290
      ]
291
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
292
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None,
293
                                None)
294

    
295
      q = self._Create(["name", "drained"])
296
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
297
      self.assertEqual(q.Query(nqd),
298
                       [[(constants.QRFS_NORMAL, "node1"),
299
                         (constants.QRFS_NORMAL, False)],
300
                        [(constants.QRFS_NORMAL, "node2"),
301
                         (constants.QRFS_NORMAL, True)],
302
                        [(constants.QRFS_NORMAL, "node3"),
303
                         (constants.QRFS_NORMAL, False)],
304
                       ])
305
      self.assertEqual(q.OldStyleQuery(nqd),
306
                       [["node1", False],
307
                        ["node2", True],
308
                        ["node3", False]])
309

    
310
  def test(self):
311
    selected = query.NODE_FIELDS.keys()
312
    field_index = dict((field, idx) for idx, field in enumerate(selected))
313

    
314
    q = self._Create(selected)
315
    self.assertEqual(q.RequestedData(),
316
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
317
                          query.NQ_GROUP, query.NQ_OOB]))
318

    
319
    cluster = objects.Cluster(cluster_name="testcluster",
320
      hvparams=constants.HVC_DEFAULTS,
321
      beparams={
322
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
323
        },
324
      nicparams={
325
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
326
        },
327
      ndparams=constants.NDC_DEFAULTS,
328
        )
329

    
330
    node_names = ["node%s" % i for i in range(20)]
331
    master_name = node_names[3]
332
    nodes = [
333
      objects.Node(name=name,
334
                   primary_ip="192.0.2.%s" % idx,
335
                   secondary_ip="192.0.100.%s" % idx,
336
                   serial_no=7789 * idx,
337
                   master_candidate=(name != master_name and idx % 3 == 0),
338
                   offline=False,
339
                   drained=False,
340
                   vm_capable=False,
341
                   master_capable=False,
342
                   ndparams={},
343
                   group="default",
344
                   ctime=1290006900,
345
                   mtime=1290006913,
346
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
347
      for idx, name in enumerate(node_names)
348
      ]
349

    
350
    master_node = nodes[3]
351
    master_node.AddTag("masternode")
352
    master_node.AddTag("another")
353
    master_node.AddTag("tag")
354
    master_node.ctime = None
355
    master_node.mtime = None
356
    assert master_node.name == master_name
357

    
358
    live_data_name = node_names[4]
359
    assert live_data_name != master_name
360

    
361
    fake_live_data = {
362
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
363
      "cnodes": 4,
364
      "csockets": 4,
365
      "ctotal": 8,
366
      "mnode": 128,
367
      "mfree": 100,
368
      "mtotal": 4096,
369
      "dfree": 5 * 1024 * 1024,
370
      "dtotal": 100 * 1024 * 1024,
371
      }
372

    
373
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
374
            sorted(fake_live_data.keys()))
375

    
376
    live_data = dict.fromkeys(node_names, {})
377
    live_data[live_data_name] = \
378
      dict((query._NODE_LIVE_FIELDS[name][2], value)
379
           for name, value in fake_live_data.items())
380

    
381
    node_to_primary = dict((name, set()) for name in node_names)
382
    node_to_primary[master_name].update(["inst1", "inst2"])
383

    
384
    node_to_secondary = dict((name, set()) for name in node_names)
385
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
386

    
387
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
388
    groups = {
389
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
390
      }
391

    
392
    oob_support = dict((name, False) for name in node_names)
393

    
394
    master_node.group = ng_uuid
395

    
396
    nqd = query.NodeQueryData(nodes, live_data, master_name,
397
                              node_to_primary, node_to_secondary, groups,
398
                              oob_support, cluster)
399
    result = q.Query(nqd)
400
    self.assert_(compat.all(len(row) == len(selected) for row in result))
401
    self.assertEqual([row[field_index["name"]] for row in result],
402
                     [(constants.QRFS_NORMAL, name) for name in node_names])
403

    
404
    node_to_row = dict((row[field_index["name"]][1], idx)
405
                       for idx, row in enumerate(result))
406

    
407
    master_row = result[node_to_row[master_name]]
408
    self.assert_(master_row[field_index["master"]])
409
    self.assert_(master_row[field_index["role"]], "M")
410
    self.assertEqual(master_row[field_index["group"]],
411
                     (constants.QRFS_NORMAL, "ng1"))
412
    self.assertEqual(master_row[field_index["group.uuid"]],
413
                     (constants.QRFS_NORMAL, ng_uuid))
414
    self.assertEqual(master_row[field_index["ctime"]],
415
                     (constants.QRFS_UNAVAIL, None))
416
    self.assertEqual(master_row[field_index["mtime"]],
417
                     (constants.QRFS_UNAVAIL, None))
418

    
419
    self.assert_(row[field_index["pip"]] == node.primary_ip and
420
                 row[field_index["sip"]] == node.secondary_ip and
421
                 set(row[field_index["tags"]]) == node.GetTags() and
422
                 row[field_index["serial_no"]] == node.serial_no and
423
                 row[field_index["role"]] == query._GetNodeRole(node,
424
                                                                master_name) and
425
                 (node.name == master_name or
426
                  (row[field_index["group"]] == "<unknown>" and
427
                   row[field_index["group.uuid"]] is None and
428
                   row[field_index["ctime"]] == (constants.QRFS_NORMAL,
429
                                                 node.ctime) and
430
                   row[field_index["mtime"]] == (constants.QRFS_NORMAL,
431
                                                 node.mtime)))
432
                 for row, node in zip(result, nodes))
433

    
434
    live_data_row = result[node_to_row[live_data_name]]
435

    
436
    for (field, value) in fake_live_data.items():
437
      self.assertEqual(live_data_row[field_index[field]],
438
                       (constants.QRFS_NORMAL, value))
439

    
440
    self.assertEqual(master_row[field_index["pinst_cnt"]],
441
                     (constants.QRFS_NORMAL, 2))
442
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
443
                     (constants.QRFS_NORMAL, 3))
444
    self.assertEqual(master_row[field_index["pinst_list"]],
445
                     (constants.QRFS_NORMAL,
446
                      list(node_to_primary[master_name])))
447
    self.assertEqual(live_data_row[field_index["sinst_list"]],
448
                     (constants.QRFS_NORMAL,
449
                      list(node_to_secondary[live_data_name])))
450

    
451
  def testGetLiveNodeField(self):
452
    nodes = [
453
      objects.Node(name="node1", drained=False, offline=False),
454
      objects.Node(name="node2", drained=True, offline=False),
455
      objects.Node(name="node3", drained=False, offline=False),
456
      objects.Node(name="node4", drained=False, offline=True),
457
      ]
458
    live_data = dict.fromkeys([node.name for node in nodes], {})
459

    
460
    # No data
461
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
462
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
463
                                             nqd, nodes[0]),
464
                     (constants.QRFS_NODATA, None))
465

    
466
    # Missing field
467
    ctx = _QueryData(None, curlive_data={
468
      "some": 1,
469
      "other": 2,
470
      })
471
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
472
                                             ctx, nodes[0]),
473
                     (constants.QRFS_UNAVAIL, None))
474

    
475
    # Wrong format/datatype
476
    ctx = _QueryData(None, curlive_data={
477
      "hello": ["Hello World"],
478
      "other": 2,
479
      })
480
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
481
                                             ctx, nodes[0]),
482
                     (constants.QRFS_UNAVAIL, None))
483

    
484
    # Offline node
485
    assert nodes[3].offline
486
    ctx = _QueryData(None, curlive_data={})
487
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
488
                                             ctx, nodes[3]),
489
                     (constants.QRFS_OFFLINE, None))
490

    
491
    # Wrong field type
492
    ctx = _QueryData(None, curlive_data={"hello": 123})
493
    self.assertRaises(AssertionError, query._GetLiveNodeField,
494
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
495

    
496

    
497
class TestInstanceQuery(unittest.TestCase):
498
  def _Create(self, selected):
499
    return query.Query(query.INSTANCE_FIELDS, selected)
500

    
501
  def testSimple(self):
502
    q = self._Create(["name", "be/memory", "ip"])
503
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
504

    
505
    cluster = objects.Cluster(cluster_name="testcluster",
506
      hvparams=constants.HVC_DEFAULTS,
507
      beparams={
508
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
509
        },
510
      nicparams={
511
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
512
        })
513

    
514
    instances = [
515
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
516
      objects.Instance(name="inst2", hvparams={}, nics=[],
517
        beparams={
518
          constants.BE_MEMORY: 512,
519
        }),
520
      objects.Instance(name="inst3", hvparams={}, beparams={},
521
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
522
      ]
523

    
524
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {})
525
    self.assertEqual(q.Query(iqd),
526
      [[(constants.QRFS_NORMAL, "inst1"),
527
        (constants.QRFS_NORMAL, 128),
528
        (constants.QRFS_UNAVAIL, None),
529
       ],
530
       [(constants.QRFS_NORMAL, "inst2"),
531
        (constants.QRFS_NORMAL, 512),
532
        (constants.QRFS_UNAVAIL, None),
533
       ],
534
       [(constants.QRFS_NORMAL, "inst3"),
535
        (constants.QRFS_NORMAL, 128),
536
        (constants.QRFS_NORMAL, "192.0.2.99"),
537
       ]])
538
    self.assertEqual(q.OldStyleQuery(iqd),
539
      [["inst1", 128, None],
540
       ["inst2", 512, None],
541
       ["inst3", 128, "192.0.2.99"]])
542

    
543
  def test(self):
544
    selected = query.INSTANCE_FIELDS.keys()
545
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
546

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

    
550
    q = self._Create(selected)
551
    self.assertEqual(q.RequestedData(),
552
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
553

    
554
    cluster = objects.Cluster(cluster_name="testcluster",
555
      hvparams=constants.HVC_DEFAULTS,
556
      beparams={
557
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
558
        },
559
      nicparams={
560
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
561
        },
562
      os_hvp={},
563
      tcpudp_port_pool=set())
564

    
565
    offline_nodes = ["nodeoff1", "nodeoff2"]
566
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
567
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
568

    
569
    instances = [
570
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
571
        uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
572
        ctime=1291244000, mtime=1291244400, serial_no=30,
573
        admin_up=True, hypervisor=constants.HT_XEN_PVM, os="linux1",
574
        primary_node="node1",
575
        disk_template=constants.DT_PLAIN,
576
        disks=[]),
577
      objects.Instance(name="inst2", hvparams={}, nics=[],
578
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
579
        ctime=1291211000, mtime=1291211077, serial_no=1,
580
        admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
581
        primary_node="node5",
582
        disk_template=constants.DT_DISKLESS,
583
        disks=[],
584
        beparams={
585
          constants.BE_MEMORY: 512,
586
        }),
587
      objects.Instance(name="inst3", hvparams={}, beparams={},
588
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
589
        ctime=1291011000, mtime=1291013000, serial_no=1923,
590
        admin_up=False, hypervisor=constants.HT_KVM, os="busybox",
591
        primary_node="node6",
592
        disk_template=constants.DT_DRBD8,
593
        disks=[],
594
        nics=[
595
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
596
                      nicparams={
597
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
598
                        }),
599
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
600
          ]),
601
      objects.Instance(name="inst4", hvparams={}, beparams={},
602
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
603
        ctime=1291244390, mtime=1291244395, serial_no=25,
604
        admin_up=False, hypervisor=constants.HT_XEN_PVM, os="linux1",
605
        primary_node="nodeoff2",
606
        disk_template=constants.DT_DRBD8,
607
        disks=[],
608
        nics=[
609
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
610
                      nicparams={
611
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
612
                        }),
613
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
614
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
615
                      nicparams={
616
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
617
                        }),
618
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
619
                      nicparams={
620
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
621
                        constants.NIC_LINK: "eth123",
622
                        }),
623
          ]),
624
      objects.Instance(name="inst5", hvparams={}, nics=[],
625
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
626
        ctime=1231211000, mtime=1261200000, serial_no=3,
627
        admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
628
        primary_node="nodebad2",
629
        disk_template=constants.DT_DISKLESS,
630
        disks=[],
631
        beparams={
632
          constants.BE_MEMORY: 512,
633
        }),
634
      objects.Instance(name="inst6", hvparams={}, nics=[],
635
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
636
        ctime=7513, mtime=11501, serial_no=13390,
637
        admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
638
        primary_node="node7",
639
        disk_template=constants.DT_DISKLESS,
640
        disks=[],
641
        beparams={
642
          constants.BE_MEMORY: 768,
643
        }),
644
      objects.Instance(name="inst7", hvparams={}, nics=[],
645
        uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
646
        ctime=None, mtime=None, serial_no=1947,
647
        admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
648
        primary_node="node6",
649
        disk_template=constants.DT_DISKLESS,
650
        disks=[],
651
        beparams={}),
652
      ]
653

    
654
    assert not utils.FindDuplicates(inst.name for inst in instances)
655

    
656
    disk_usage = dict((inst.name,
657
                       cmdlib._ComputeDiskSize(inst.disk_template,
658
                                               [{"size": disk.size}
659
                                                for disk in inst.disks]))
660
                      for inst in instances)
661

    
662
    inst_bridges = {
663
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
664
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
665
                None, "eth123"],
666
      }
667

    
668
    live_data = {
669
      "inst2": {
670
        "vcpus": 3,
671
        },
672
      "inst4": {
673
        "memory": 123,
674
        },
675
      "inst6": {
676
        "memory": 768,
677
        },
678
      }
679

    
680
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
681
                                  offline_nodes, bad_nodes, live_data)
682
    result = q.Query(iqd)
683
    self.assertEqual(len(result), len(instances))
684
    self.assert_(compat.all(len(row) == len(selected)
685
                            for row in result))
686

    
687
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
688
           "Offline nodes not included in bad nodes"
689

    
690
    tested_status = set()
691

    
692
    for (inst, row) in zip(instances, result):
693
      assert inst.primary_node in nodes
694

    
695
      self.assertEqual(row[fieldidx["name"]],
696
                       (constants.QRFS_NORMAL, inst.name))
697

    
698
      if inst.primary_node in offline_nodes:
699
        exp_status = "ERROR_nodeoffline"
700
      elif inst.primary_node in bad_nodes:
701
        exp_status = "ERROR_nodedown"
702
      elif inst.name in live_data:
703
        if inst.admin_up:
704
          exp_status = "running"
705
        else:
706
          exp_status = "ERROR_up"
707
      elif inst.admin_up:
708
        exp_status = "ERROR_down"
709
      else:
710
        exp_status = "ADMIN_down"
711

    
712
      self.assertEqual(row[fieldidx["status"]],
713
                       (constants.QRFS_NORMAL, exp_status))
714

    
715
      (_, status) = row[fieldidx["status"]]
716
      tested_status.add(status)
717

    
718
      for (field, livefield) in [("oper_ram", "memory"),
719
                                 ("oper_vcpus", "vcpus")]:
720
        if inst.primary_node in bad_nodes:
721
          exp = (constants.QRFS_NODATA, None)
722
        elif inst.name in live_data:
723
          value = live_data[inst.name].get(livefield, None)
724
          if value is None:
725
            exp = (constants.QRFS_UNAVAIL, None)
726
          else:
727
            exp = (constants.QRFS_NORMAL, value)
728
        else:
729
          exp = (constants.QRFS_UNAVAIL, None)
730

    
731
        self.assertEqual(row[fieldidx[field]], exp)
732

    
733
      bridges = inst_bridges.get(inst.name, [])
734
      self.assertEqual(row[fieldidx["nic.bridges"]],
735
                       (constants.QRFS_NORMAL, bridges))
736
      if bridges:
737
        self.assertEqual(row[fieldidx["bridge"]],
738
                         (constants.QRFS_NORMAL, bridges[0]))
739
      else:
740
        self.assertEqual(row[fieldidx["bridge"]],
741
                         (constants.QRFS_UNAVAIL, None))
742

    
743
      for i in range(constants.MAX_NICS):
744
        if i < len(bridges) and bridges[i] is not None:
745
          exp = (constants.QRFS_NORMAL, bridges[i])
746
        else:
747
          exp = (constants.QRFS_UNAVAIL, None)
748
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
749

    
750
      if inst.primary_node in bad_nodes:
751
        exp = (constants.QRFS_NODATA, None)
752
      else:
753
        exp = (constants.QRFS_NORMAL, inst.name in live_data)
754
      self.assertEqual(row[fieldidx["oper_state"]], exp)
755

    
756
      usage = disk_usage[inst.name]
757
      if usage is None:
758
        usage = 0
759
      self.assertEqual(row[fieldidx["disk_usage"]],
760
                       (constants.QRFS_NORMAL, usage))
761

    
762
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
763
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
764

    
765
      for field in ["ctime", "mtime"]:
766
        if getattr(inst, field) is None:
767
          # No ctime/mtime
768
          exp = (constants.QRFS_UNAVAIL, None)
769
        else:
770
          exp = (constants.QRFS_NORMAL, getattr(inst, field))
771
        self.assertEqual(row[fieldidx[field]], exp)
772

    
773
    # Ensure all possible status' have been tested
774
    self.assertEqual(tested_status,
775
                     set(["ERROR_nodeoffline", "ERROR_nodedown",
776
                          "running", "ERROR_up", "ERROR_down",
777
                          "ADMIN_down"]))
778

    
779

    
780
class TestGroupQuery(unittest.TestCase):
781

    
782
  def setUp(self):
783
    self.groups = [
784
      objects.NodeGroup(name="default",
785
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
786
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
787
      objects.NodeGroup(name="restricted",
788
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
789
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
790
      ]
791

    
792
  def _Create(self, selected):
793
    return query.Query(query.GROUP_FIELDS, selected)
794

    
795
  def testSimple(self):
796
    q = self._Create(["name", "uuid", "alloc_policy"])
797
    gqd = query.GroupQueryData(self.groups, None, None)
798

    
799
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
800

    
801
    self.assertEqual(q.Query(gqd),
802
      [[(constants.QRFS_NORMAL, "default"),
803
        (constants.QRFS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
804
        (constants.QRFS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
805
        ],
806
       [(constants.QRFS_NORMAL, "restricted"),
807
        (constants.QRFS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
808
        (constants.QRFS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
809
        ],
810
       ])
811

    
812
  def testNodes(self):
813
    groups_to_nodes = {
814
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
815
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
816
      }
817

    
818
    q = self._Create(["name", "node_cnt", "node_list"])
819
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
820

    
821
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
822

    
823
    self.assertEqual(q.Query(gqd),
824
                     [[(constants.QRFS_NORMAL, "default"),
825
                       (constants.QRFS_NORMAL, 2),
826
                       (constants.QRFS_NORMAL, ["node1", "node2"]),
827
                       ],
828
                      [(constants.QRFS_NORMAL, "restricted"),
829
                       (constants.QRFS_NORMAL, 3),
830
                       (constants.QRFS_NORMAL, ["node1", "node9", "node10"]),
831
                       ],
832
                      ])
833

    
834
  def testInstances(self):
835
    groups_to_instances = {
836
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
837
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
838
      }
839

    
840
    q = self._Create(["pinst_cnt", "pinst_list"])
841
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
842

    
843
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
844

    
845
    self.assertEqual(q.Query(gqd),
846
                     [[(constants.QRFS_NORMAL, 2),
847
                       (constants.QRFS_NORMAL, ["inst1", "inst2"]),
848
                       ],
849
                      [(constants.QRFS_NORMAL, 3),
850
                       (constants.QRFS_NORMAL, ["inst1", "inst9", "inst10"]),
851
                       ],
852
                      ])
853

    
854

    
855
class TestQueryFields(unittest.TestCase):
856
  def testAllFields(self):
857
    for fielddefs in query.ALL_FIELD_LISTS:
858
      result = query.QueryFields(fielddefs, None)
859
      self.assert_(isinstance(result, dict))
860
      response = objects.QueryFieldsResponse.FromDict(result)
861
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
862
        [(fdef2.name, fdef2.title)
863
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
864
                                             key=lambda x: x[0].name)])
865

    
866
  def testSomeFields(self):
867
    rnd = random.Random(5357)
868

    
869
    for _ in range(10):
870
      for fielddefs in query.ALL_FIELD_LISTS:
871
        if len(fielddefs) > 20:
872
          sample_size = rnd.randint(5, 20)
873
        else:
874
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
875
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
876
                                                      sample_size)]
877
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
878
        self.assert_(isinstance(result, dict))
879
        response = objects.QueryFieldsResponse.FromDict(result)
880
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
881
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
882

    
883

    
884
if __name__ == "__main__":
885
  testutils.GanetiTestProgram()