Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ e571ee44

History | View | Annotate | Download (30 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)
293

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

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

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

    
318
    node_names = ["node%s" % i for i in range(20)]
319
    master_name = node_names[3]
320
    nodes = [
321
      objects.Node(name=name,
322
                   primary_ip="192.0.2.%s" % idx,
323
                   secondary_ip="192.0.100.%s" % idx,
324
                   serial_no=7789 * idx,
325
                   master_candidate=(name != master_name and idx % 3 == 0),
326
                   offline=False,
327
                   drained=False,
328
                   vm_capable=False,
329
                   master_capable=False,
330
                   group="default",
331
                   ctime=1290006900,
332
                   mtime=1290006913,
333
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
334
      for idx, name in enumerate(node_names)
335
      ]
336

    
337
    master_node = nodes[3]
338
    master_node.AddTag("masternode")
339
    master_node.AddTag("another")
340
    master_node.AddTag("tag")
341
    master_node.ctime = None
342
    master_node.mtime = None
343
    assert master_node.name == master_name
344

    
345
    live_data_name = node_names[4]
346
    assert live_data_name != master_name
347

    
348
    fake_live_data = {
349
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
350
      "cnodes": 4,
351
      "csockets": 4,
352
      "ctotal": 8,
353
      "mnode": 128,
354
      "mfree": 100,
355
      "mtotal": 4096,
356
      "dfree": 5 * 1024 * 1024,
357
      "dtotal": 100 * 1024 * 1024,
358
      }
359

    
360
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
361
            sorted(fake_live_data.keys()))
362

    
363
    live_data = dict.fromkeys(node_names, {})
364
    live_data[live_data_name] = \
365
      dict((query._NODE_LIVE_FIELDS[name][2], value)
366
           for name, value in fake_live_data.items())
367

    
368
    node_to_primary = dict((name, set()) for name in node_names)
369
    node_to_primary[master_name].update(["inst1", "inst2"])
370

    
371
    node_to_secondary = dict((name, set()) for name in node_names)
372
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
373

    
374
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
375
    groups = {
376
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid),
377
      }
378

    
379
    master_node.group = ng_uuid
380

    
381
    nqd = query.NodeQueryData(nodes, live_data, master_name,
382
                              node_to_primary, node_to_secondary, groups)
383
    result = q.Query(nqd)
384
    self.assert_(compat.all(len(row) == len(selected) for row in result))
385
    self.assertEqual([row[field_index["name"]] for row in result],
386
                     [(constants.QRFS_NORMAL, name) for name in node_names])
387

    
388
    node_to_row = dict((row[field_index["name"]][1], idx)
389
                       for idx, row in enumerate(result))
390

    
391
    master_row = result[node_to_row[master_name]]
392
    self.assert_(master_row[field_index["master"]])
393
    self.assert_(master_row[field_index["role"]], "M")
394
    self.assertEqual(master_row[field_index["group"]],
395
                     (constants.QRFS_NORMAL, "ng1"))
396
    self.assertEqual(master_row[field_index["group.uuid"]],
397
                     (constants.QRFS_NORMAL, ng_uuid))
398
    self.assertEqual(master_row[field_index["ctime"]],
399
                     (constants.QRFS_UNAVAIL, None))
400
    self.assertEqual(master_row[field_index["mtime"]],
401
                     (constants.QRFS_UNAVAIL, None))
402

    
403
    self.assert_(row[field_index["pip"]] == node.primary_ip and
404
                 row[field_index["sip"]] == node.secondary_ip and
405
                 set(row[field_index["tags"]]) == node.GetTags() and
406
                 row[field_index["serial_no"]] == node.serial_no and
407
                 row[field_index["role"]] == query._GetNodeRole(node,
408
                                                                master_name) and
409
                 (node.name == master_name or
410
                  (row[field_index["group"]] == "<unknown>" and
411
                   row[field_index["group.uuid"]] is None and
412
                   row[field_index["ctime"]] == (constants.QRFS_NORMAL,
413
                                                 node.ctime) and
414
                   row[field_index["mtime"]] == (constants.QRFS_NORMAL,
415
                                                 node.mtime)))
416
                 for row, node in zip(result, nodes))
417

    
418
    live_data_row = result[node_to_row[live_data_name]]
419

    
420
    for (field, value) in fake_live_data.items():
421
      self.assertEqual(live_data_row[field_index[field]],
422
                       (constants.QRFS_NORMAL, value))
423

    
424
    self.assertEqual(master_row[field_index["pinst_cnt"]],
425
                     (constants.QRFS_NORMAL, 2))
426
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
427
                     (constants.QRFS_NORMAL, 3))
428
    self.assertEqual(master_row[field_index["pinst_list"]],
429
                     (constants.QRFS_NORMAL,
430
                      list(node_to_primary[master_name])))
431
    self.assertEqual(live_data_row[field_index["sinst_list"]],
432
                     (constants.QRFS_NORMAL,
433
                      list(node_to_secondary[live_data_name])))
434

    
435
  def testGetLiveNodeField(self):
436
    nodes = [
437
      objects.Node(name="node1", drained=False, offline=False),
438
      objects.Node(name="node2", drained=True, offline=False),
439
      objects.Node(name="node3", drained=False, offline=False),
440
      objects.Node(name="node4", drained=False, offline=True),
441
      ]
442
    live_data = dict.fromkeys([node.name for node in nodes], {})
443

    
444
    # No data
445
    nqd = query.NodeQueryData(None, None, None, None, None, None)
446
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
447
                                             nqd, nodes[0]),
448
                     (constants.QRFS_NODATA, None))
449

    
450
    # Missing field
451
    ctx = _QueryData(None, curlive_data={
452
      "some": 1,
453
      "other": 2,
454
      })
455
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
456
                                             ctx, nodes[0]),
457
                     (constants.QRFS_UNAVAIL, None))
458

    
459
    # Wrong format/datatype
460
    ctx = _QueryData(None, curlive_data={
461
      "hello": ["Hello World"],
462
      "other": 2,
463
      })
464
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
465
                                             ctx, nodes[0]),
466
                     (constants.QRFS_UNAVAIL, None))
467

    
468
    # Offline node
469
    assert nodes[3].offline
470
    ctx = _QueryData(None, curlive_data={})
471
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
472
                                             ctx, nodes[3]),
473
                     (constants.QRFS_OFFLINE, None))
474

    
475
    # Wrong field type
476
    ctx = _QueryData(None, curlive_data={"hello": 123})
477
    self.assertRaises(AssertionError, query._GetLiveNodeField,
478
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
479

    
480

    
481
class TestInstanceQuery(unittest.TestCase):
482
  def _Create(self, selected):
483
    return query.Query(query.INSTANCE_FIELDS, selected)
484

    
485
  def testSimple(self):
486
    q = self._Create(["name", "be/memory", "ip"])
487
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
488

    
489
    cluster = objects.Cluster(cluster_name="testcluster",
490
      hvparams=constants.HVC_DEFAULTS,
491
      beparams={
492
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
493
        },
494
      nicparams={
495
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
496
        })
497

    
498
    instances = [
499
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
500
      objects.Instance(name="inst2", hvparams={}, nics=[],
501
        beparams={
502
          constants.BE_MEMORY: 512,
503
        }),
504
      objects.Instance(name="inst3", hvparams={}, beparams={},
505
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
506
      ]
507

    
508
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {})
509
    self.assertEqual(q.Query(iqd),
510
      [[(constants.QRFS_NORMAL, "inst1"),
511
        (constants.QRFS_NORMAL, 128),
512
        (constants.QRFS_UNAVAIL, None),
513
       ],
514
       [(constants.QRFS_NORMAL, "inst2"),
515
        (constants.QRFS_NORMAL, 512),
516
        (constants.QRFS_UNAVAIL, None),
517
       ],
518
       [(constants.QRFS_NORMAL, "inst3"),
519
        (constants.QRFS_NORMAL, 128),
520
        (constants.QRFS_NORMAL, "192.0.2.99"),
521
       ]])
522
    self.assertEqual(q.OldStyleQuery(iqd),
523
      [["inst1", 128, None],
524
       ["inst2", 512, None],
525
       ["inst3", 128, "192.0.2.99"]])
526

    
527
  def test(self):
528
    selected = query.INSTANCE_FIELDS.keys()
529
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
530

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

    
534
    q = self._Create(selected)
535
    self.assertEqual(q.RequestedData(),
536
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
537

    
538
    cluster = objects.Cluster(cluster_name="testcluster",
539
      hvparams=constants.HVC_DEFAULTS,
540
      beparams={
541
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
542
        },
543
      nicparams={
544
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
545
        },
546
      os_hvp={},
547
      tcpudp_port_pool=set())
548

    
549
    offline_nodes = ["nodeoff1", "nodeoff2"]
550
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
551
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
552

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

    
638
    assert not utils.FindDuplicates(inst.name for inst in instances)
639

    
640
    disk_usage = dict((inst.name,
641
                       cmdlib._ComputeDiskSize(inst.disk_template,
642
                                               [{"size": disk.size}
643
                                                for disk in inst.disks]))
644
                      for inst in instances)
645

    
646
    inst_bridges = {
647
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
648
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
649
                None, "eth123"],
650
      }
651

    
652
    live_data = {
653
      "inst2": {
654
        "vcpus": 3,
655
        },
656
      "inst4": {
657
        "memory": 123,
658
        },
659
      "inst6": {
660
        "memory": 768,
661
        },
662
      }
663

    
664
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
665
                                  offline_nodes, bad_nodes, live_data)
666
    result = q.Query(iqd)
667
    self.assertEqual(len(result), len(instances))
668
    self.assert_(compat.all(len(row) == len(selected)
669
                            for row in result))
670

    
671
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
672
           "Offline nodes not included in bad nodes"
673

    
674
    tested_status = set()
675

    
676
    for (inst, row) in zip(instances, result):
677
      assert inst.primary_node in nodes
678

    
679
      self.assertEqual(row[fieldidx["name"]],
680
                       (constants.QRFS_NORMAL, inst.name))
681

    
682
      if inst.primary_node in offline_nodes:
683
        exp_status = "ERROR_nodeoffline"
684
      elif inst.primary_node in bad_nodes:
685
        exp_status = "ERROR_nodedown"
686
      elif inst.name in live_data:
687
        if inst.admin_up:
688
          exp_status = "running"
689
        else:
690
          exp_status = "ERROR_up"
691
      elif inst.admin_up:
692
        exp_status = "ERROR_down"
693
      else:
694
        exp_status = "ADMIN_down"
695

    
696
      self.assertEqual(row[fieldidx["status"]],
697
                       (constants.QRFS_NORMAL, exp_status))
698

    
699
      (_, status) = row[fieldidx["status"]]
700
      tested_status.add(status)
701

    
702
      for (field, livefield) in [("oper_ram", "memory"),
703
                                 ("oper_vcpus", "vcpus")]:
704
        if inst.primary_node in bad_nodes:
705
          exp = (constants.QRFS_NODATA, None)
706
        elif inst.name in live_data:
707
          value = live_data[inst.name].get(livefield, None)
708
          if value is None:
709
            exp = (constants.QRFS_UNAVAIL, None)
710
          else:
711
            exp = (constants.QRFS_NORMAL, value)
712
        else:
713
          exp = (constants.QRFS_UNAVAIL, None)
714

    
715
        self.assertEqual(row[fieldidx[field]], exp)
716

    
717
      bridges = inst_bridges.get(inst.name, [])
718
      self.assertEqual(row[fieldidx["nic.bridges"]],
719
                       (constants.QRFS_NORMAL, bridges))
720
      if bridges:
721
        self.assertEqual(row[fieldidx["bridge"]],
722
                         (constants.QRFS_NORMAL, bridges[0]))
723
      else:
724
        self.assertEqual(row[fieldidx["bridge"]],
725
                         (constants.QRFS_UNAVAIL, None))
726

    
727
      for i in range(constants.MAX_NICS):
728
        if i < len(bridges) and bridges[i] is not None:
729
          exp = (constants.QRFS_NORMAL, bridges[i])
730
        else:
731
          exp = (constants.QRFS_UNAVAIL, None)
732
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
733

    
734
      if inst.primary_node in bad_nodes:
735
        exp = (constants.QRFS_NODATA, None)
736
      else:
737
        exp = (constants.QRFS_NORMAL, inst.name in live_data)
738
      self.assertEqual(row[fieldidx["oper_state"]], exp)
739

    
740
      usage = disk_usage[inst.name]
741
      if usage is None:
742
        usage = 0
743
      self.assertEqual(row[fieldidx["disk_usage"]],
744
                       (constants.QRFS_NORMAL, usage))
745

    
746
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
747
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
748

    
749
      for field in ["ctime", "mtime"]:
750
        if getattr(inst, field) is None:
751
          # No ctime/mtime
752
          exp = (constants.QRFS_UNAVAIL, None)
753
        else:
754
          exp = (constants.QRFS_NORMAL, getattr(inst, field))
755
        self.assertEqual(row[fieldidx[field]], exp)
756

    
757
    # Ensure all possible status' have been tested
758
    self.assertEqual(tested_status,
759
                     set(["ERROR_nodeoffline", "ERROR_nodedown",
760
                          "running", "ERROR_up", "ERROR_down",
761
                          "ADMIN_down"]))
762

    
763

    
764
class TestQueryFields(unittest.TestCase):
765
  def testAllFields(self):
766
    for fielddefs in query.ALL_FIELD_LISTS:
767
      result = query.QueryFields(fielddefs, None)
768
      self.assert_(isinstance(result, dict))
769
      response = objects.QueryFieldsResponse.FromDict(result)
770
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
771
        [(fdef2.name, fdef2.title)
772
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
773
                                             key=lambda x: x[0].name)])
774

    
775
  def testSomeFields(self):
776
    rnd = random.Random(5357)
777

    
778
    for _ in range(10):
779
      for fielddefs in query.ALL_FIELD_LISTS:
780
        if len(fielddefs) > 20:
781
          sample_size = rnd.randint(5, 20)
782
        else:
783
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
784
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
785
                                                      sample_size)]
786
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
787
        self.assert_(isinstance(result, dict))
788
        response = objects.QueryFieldsResponse.FromDict(result)
789
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
790
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
791

    
792

    
793
if __name__ == "__main__":
794
  testutils.GanetiTestProgram()