Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ aa29e95f

History | View | Annotate | Download (29.5 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),
438
      objects.Node(name="node2", drained=True),
439
      objects.Node(name="node3", drained=False),
440
      ]
441
    live_data = dict.fromkeys([node.name for node in nodes], {})
442

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

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

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

    
467
    # Wrong field type
468
    ctx = _QueryData(None, curlive_data={"hello": 123})
469
    self.assertRaises(AssertionError, query._GetLiveNodeField,
470
                      "hello", constants.QFT_BOOL, ctx, None)
471

    
472

    
473
class TestInstanceQuery(unittest.TestCase):
474
  def _Create(self, selected):
475
    return query.Query(query.INSTANCE_FIELDS, selected)
476

    
477
  def testSimple(self):
478
    q = self._Create(["name", "be/memory", "ip"])
479
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
480

    
481
    cluster = objects.Cluster(cluster_name="testcluster",
482
      hvparams=constants.HVC_DEFAULTS,
483
      beparams={
484
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
485
        },
486
      nicparams={
487
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
488
        })
489

    
490
    instances = [
491
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
492
      objects.Instance(name="inst2", hvparams={}, nics=[],
493
        beparams={
494
          constants.BE_MEMORY: 512,
495
        }),
496
      objects.Instance(name="inst3", hvparams={}, beparams={},
497
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
498
      ]
499

    
500
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {})
501
    self.assertEqual(q.Query(iqd),
502
      [[(constants.QRFS_NORMAL, "inst1"),
503
        (constants.QRFS_NORMAL, 128),
504
        (constants.QRFS_UNAVAIL, None),
505
       ],
506
       [(constants.QRFS_NORMAL, "inst2"),
507
        (constants.QRFS_NORMAL, 512),
508
        (constants.QRFS_UNAVAIL, None),
509
       ],
510
       [(constants.QRFS_NORMAL, "inst3"),
511
        (constants.QRFS_NORMAL, 128),
512
        (constants.QRFS_NORMAL, "192.0.2.99"),
513
       ]])
514
    self.assertEqual(q.OldStyleQuery(iqd),
515
      [["inst1", 128, None],
516
       ["inst2", 512, None],
517
       ["inst3", 128, "192.0.2.99"]])
518

    
519
  def test(self):
520
    selected = query.INSTANCE_FIELDS.keys()
521
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
522

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

    
526
    q = self._Create(selected)
527
    self.assertEqual(q.RequestedData(),
528
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
529

    
530
    cluster = objects.Cluster(cluster_name="testcluster",
531
      hvparams=constants.HVC_DEFAULTS,
532
      beparams={
533
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
534
        },
535
      nicparams={
536
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
537
        },
538
      os_hvp={},
539
      tcpudp_port_pool=set())
540

    
541
    offline_nodes = ["nodeoff1", "nodeoff2"]
542
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
543
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
544

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

    
630
    assert not utils.FindDuplicates(inst.name for inst in instances)
631

    
632
    disk_usage = dict((inst.name,
633
                       cmdlib._ComputeDiskSize(inst.disk_template,
634
                                               [{"size": disk.size}
635
                                                for disk in inst.disks]))
636
                      for inst in instances)
637

    
638
    inst_bridges = {
639
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
640
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
641
                None, "eth123"],
642
      }
643

    
644
    live_data = {
645
      "inst2": {
646
        "vcpus": 3,
647
        },
648
      "inst4": {
649
        "memory": 123,
650
        },
651
      "inst6": {
652
        "memory": 768,
653
        },
654
      }
655

    
656
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
657
                                  offline_nodes, bad_nodes, live_data)
658
    result = q.Query(iqd)
659
    self.assertEqual(len(result), len(instances))
660
    self.assert_(compat.all(len(row) == len(selected)
661
                            for row in result))
662

    
663
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
664
           "Offline nodes not included in bad nodes"
665

    
666
    tested_status = set()
667

    
668
    for (inst, row) in zip(instances, result):
669
      assert inst.primary_node in nodes
670

    
671
      self.assertEqual(row[fieldidx["name"]],
672
                       (constants.QRFS_NORMAL, inst.name))
673

    
674
      if inst.primary_node in offline_nodes:
675
        exp_status = "ERROR_nodeoffline"
676
      elif inst.primary_node in bad_nodes:
677
        exp_status = "ERROR_nodedown"
678
      elif inst.name in live_data:
679
        if inst.admin_up:
680
          exp_status = "running"
681
        else:
682
          exp_status = "ERROR_up"
683
      elif inst.admin_up:
684
        exp_status = "ERROR_down"
685
      else:
686
        exp_status = "ADMIN_down"
687

    
688
      self.assertEqual(row[fieldidx["status"]],
689
                       (constants.QRFS_NORMAL, exp_status))
690

    
691
      (_, status) = row[fieldidx["status"]]
692
      tested_status.add(status)
693

    
694
      for (field, livefield) in [("oper_ram", "memory"),
695
                                 ("oper_vcpus", "vcpus")]:
696
        if inst.primary_node in bad_nodes:
697
          exp = (constants.QRFS_NODATA, None)
698
        elif inst.name in live_data:
699
          value = live_data[inst.name].get(livefield, None)
700
          if value is None:
701
            exp = (constants.QRFS_UNAVAIL, None)
702
          else:
703
            exp = (constants.QRFS_NORMAL, value)
704
        else:
705
          exp = (constants.QRFS_UNAVAIL, None)
706

    
707
        self.assertEqual(row[fieldidx[field]], exp)
708

    
709
      bridges = inst_bridges.get(inst.name, [])
710
      self.assertEqual(row[fieldidx["nic.bridges"]],
711
                       (constants.QRFS_NORMAL, bridges))
712
      if bridges:
713
        self.assertEqual(row[fieldidx["bridge"]],
714
                         (constants.QRFS_NORMAL, bridges[0]))
715
      else:
716
        self.assertEqual(row[fieldidx["bridge"]],
717
                         (constants.QRFS_UNAVAIL, None))
718

    
719
      for i in range(constants.MAX_NICS):
720
        if i < len(bridges) and bridges[i] is not None:
721
          exp = (constants.QRFS_NORMAL, bridges[i])
722
        else:
723
          exp = (constants.QRFS_UNAVAIL, None)
724
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
725

    
726
      if inst.primary_node in bad_nodes:
727
        exp = (constants.QRFS_NODATA, None)
728
      else:
729
        exp = (constants.QRFS_NORMAL, inst.name in live_data)
730
      self.assertEqual(row[fieldidx["oper_state"]], exp)
731

    
732
      usage = disk_usage[inst.name]
733
      if usage is None:
734
        usage = 0
735
      self.assertEqual(row[fieldidx["disk_usage"]],
736
                       (constants.QRFS_NORMAL, usage))
737

    
738
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
739
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
740

    
741
      for field in ["ctime", "mtime"]:
742
        if getattr(inst, field) is None:
743
          # No ctime/mtime
744
          exp = (constants.QRFS_UNAVAIL, None)
745
        else:
746
          exp = (constants.QRFS_NORMAL, getattr(inst, field))
747
        self.assertEqual(row[fieldidx[field]], exp)
748

    
749
    # Ensure all possible status' have been tested
750
    self.assertEqual(tested_status,
751
                     set(["ERROR_nodeoffline", "ERROR_nodedown",
752
                          "running", "ERROR_up", "ERROR_down",
753
                          "ADMIN_down"]))
754

    
755

    
756
class TestQueryFields(unittest.TestCase):
757
  def testAllFields(self):
758
    for fielddefs in [query.NODE_FIELDS, query.INSTANCE_FIELDS]:
759
      result = query.QueryFields(fielddefs, None)
760
      self.assert_(isinstance(result, dict))
761
      response = objects.QueryFieldsResponse.FromDict(result)
762
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
763
        [(fdef2.name, fdef2.title)
764
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
765
                                             key=lambda x: x[0].name)])
766

    
767
  def testSomeFields(self):
768
    rnd = random.Random(5357)
769

    
770
    for _ in range(10):
771
      for fielddefs in [query.NODE_FIELDS, query.INSTANCE_FIELDS]:
772
        fields = [fdef
773
                  for (fdef, _, _) in rnd.sample(fielddefs.values(),
774
                                                 rnd.randint(5, 20))]
775
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
776
        self.assert_(isinstance(result, dict))
777
        response = objects.QueryFieldsResponse.FromDict(result)
778
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
779
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
780

    
781

    
782
if __name__ == "__main__":
783
  testutils.GanetiTestProgram()