Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ effab4ca

History | View | Annotate | Download (35.2 kB)

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

    
4
# Copyright (C) 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for testing ganeti.query"""
23

    
24
import re
25
import unittest
26
import random
27

    
28
from ganeti import constants
29
from ganeti import utils
30
from ganeti import compat
31
from ganeti import errors
32
from ganeti import query
33
from ganeti import objects
34
from ganeti import cmdlib
35

    
36
import testutils
37

    
38

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

    
44

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

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

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

    
55

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

    
63

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
257
  def testAliases(self):
258
    fields = [
259
      (query._MakeField("a", "a-title", constants.QFT_TEXT), None,
260
       lambda *args: None),
261
      (query._MakeField("b", "b-title", constants.QFT_TEXT), None,
262
       lambda *args: None),
263
      ]
264
    # duplicate field
265
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
266
                      [("b", "a")])
267
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
268
                      [("c", "b"), ("c", "a")])
269
    # missing target
270
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
271
                      [("c", "d")])
272
    fdefs = query._PrepareFieldList(fields, [("c", "b")])
273
    self.assertEqual(len(fdefs), 3)
274
    self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
275

    
276

    
277
class TestGetNodeRole(unittest.TestCase):
278
  def testMaster(self):
279
    node = objects.Node(name="node1")
280
    self.assertEqual(query._GetNodeRole(node, "node1"), "M")
281

    
282
  def testMasterCandidate(self):
283
    node = objects.Node(name="node1", master_candidate=True)
284
    self.assertEqual(query._GetNodeRole(node, "master"), "C")
285

    
286
  def testRegular(self):
287
    node = objects.Node(name="node1")
288
    self.assertEqual(query._GetNodeRole(node, "master"), "R")
289

    
290
  def testDrained(self):
291
    node = objects.Node(name="node1", drained=True)
292
    self.assertEqual(query._GetNodeRole(node, "master"), "D")
293

    
294
  def testOffline(self):
295
    node = objects.Node(name="node1", offline=True)
296
    self.assertEqual(query._GetNodeRole(node, "master"), "O")
297

    
298

    
299
class TestNodeQuery(unittest.TestCase):
300
  def _Create(self, selected):
301
    return query.Query(query.NODE_FIELDS, selected)
302

    
303
  def testSimple(self):
304
    nodes = [
305
      objects.Node(name="node1", drained=False),
306
      objects.Node(name="node2", drained=True),
307
      objects.Node(name="node3", drained=False),
308
      ]
309
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
310
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None,
311
                                None)
312

    
313
      q = self._Create(["name", "drained"])
314
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
315
      self.assertEqual(q.Query(nqd),
316
                       [[(constants.RS_NORMAL, "node1"),
317
                         (constants.RS_NORMAL, False)],
318
                        [(constants.RS_NORMAL, "node2"),
319
                         (constants.RS_NORMAL, True)],
320
                        [(constants.RS_NORMAL, "node3"),
321
                         (constants.RS_NORMAL, False)],
322
                       ])
323
      self.assertEqual(q.OldStyleQuery(nqd),
324
                       [["node1", False],
325
                        ["node2", True],
326
                        ["node3", False]])
327

    
328
  def test(self):
329
    selected = query.NODE_FIELDS.keys()
330
    field_index = dict((field, idx) for idx, field in enumerate(selected))
331

    
332
    q = self._Create(selected)
333
    self.assertEqual(q.RequestedData(),
334
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
335
                          query.NQ_GROUP, query.NQ_OOB]))
336

    
337
    cluster = objects.Cluster(cluster_name="testcluster",
338
      hvparams=constants.HVC_DEFAULTS,
339
      beparams={
340
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
341
        },
342
      nicparams={
343
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
344
        },
345
      ndparams=constants.NDC_DEFAULTS,
346
        )
347

    
348
    node_names = ["node%s" % i for i in range(20)]
349
    master_name = node_names[3]
350
    nodes = [
351
      objects.Node(name=name,
352
                   primary_ip="192.0.2.%s" % idx,
353
                   secondary_ip="192.0.100.%s" % idx,
354
                   serial_no=7789 * idx,
355
                   master_candidate=(name != master_name and idx % 3 == 0),
356
                   offline=False,
357
                   drained=False,
358
                   vm_capable=True,
359
                   master_capable=False,
360
                   ndparams={},
361
                   group="default",
362
                   ctime=1290006900,
363
                   mtime=1290006913,
364
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
365
      for idx, name in enumerate(node_names)
366
      ]
367

    
368
    master_node = nodes[3]
369
    master_node.AddTag("masternode")
370
    master_node.AddTag("another")
371
    master_node.AddTag("tag")
372
    master_node.ctime = None
373
    master_node.mtime = None
374
    assert master_node.name == master_name
375

    
376
    live_data_name = node_names[4]
377
    assert live_data_name != master_name
378

    
379
    fake_live_data = {
380
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
381
      "cnodes": 4,
382
      "csockets": 4,
383
      "ctotal": 8,
384
      "mnode": 128,
385
      "mfree": 100,
386
      "mtotal": 4096,
387
      "dfree": 5 * 1024 * 1024,
388
      "dtotal": 100 * 1024 * 1024,
389
      }
390

    
391
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
392
            sorted(fake_live_data.keys()))
393

    
394
    live_data = dict.fromkeys(node_names, {})
395
    live_data[live_data_name] = \
396
      dict((query._NODE_LIVE_FIELDS[name][2], value)
397
           for name, value in fake_live_data.items())
398

    
399
    node_to_primary = dict((name, set()) for name in node_names)
400
    node_to_primary[master_name].update(["inst1", "inst2"])
401

    
402
    node_to_secondary = dict((name, set()) for name in node_names)
403
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
404

    
405
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
406
    groups = {
407
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
408
      }
409

    
410
    oob_support = dict((name, False) for name in node_names)
411

    
412
    master_node.group = ng_uuid
413

    
414
    nqd = query.NodeQueryData(nodes, live_data, master_name,
415
                              node_to_primary, node_to_secondary, groups,
416
                              oob_support, cluster)
417
    result = q.Query(nqd)
418
    self.assert_(compat.all(len(row) == len(selected) for row in result))
419
    self.assertEqual([row[field_index["name"]] for row in result],
420
                     [(constants.RS_NORMAL, name) for name in node_names])
421

    
422
    node_to_row = dict((row[field_index["name"]][1], idx)
423
                       for idx, row in enumerate(result))
424

    
425
    master_row = result[node_to_row[master_name]]
426
    self.assert_(master_row[field_index["master"]])
427
    self.assert_(master_row[field_index["role"]], "M")
428
    self.assertEqual(master_row[field_index["group"]],
429
                     (constants.RS_NORMAL, "ng1"))
430
    self.assertEqual(master_row[field_index["group.uuid"]],
431
                     (constants.RS_NORMAL, ng_uuid))
432
    self.assertEqual(master_row[field_index["ctime"]],
433
                     (constants.RS_UNAVAIL, None))
434
    self.assertEqual(master_row[field_index["mtime"]],
435
                     (constants.RS_UNAVAIL, None))
436

    
437
    self.assert_(row[field_index["pip"]] == node.primary_ip and
438
                 row[field_index["sip"]] == node.secondary_ip and
439
                 set(row[field_index["tags"]]) == node.GetTags() and
440
                 row[field_index["serial_no"]] == node.serial_no and
441
                 row[field_index["role"]] == query._GetNodeRole(node,
442
                                                                master_name) and
443
                 (node.name == master_name or
444
                  (row[field_index["group"]] == "<unknown>" and
445
                   row[field_index["group.uuid"]] is None and
446
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
447
                                                 node.ctime) and
448
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
449
                                                 node.mtime)))
450
                 for row, node in zip(result, nodes))
451

    
452
    live_data_row = result[node_to_row[live_data_name]]
453

    
454
    for (field, value) in fake_live_data.items():
455
      self.assertEqual(live_data_row[field_index[field]],
456
                       (constants.RS_NORMAL, value))
457

    
458
    self.assertEqual(master_row[field_index["pinst_cnt"]],
459
                     (constants.RS_NORMAL, 2))
460
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
461
                     (constants.RS_NORMAL, 3))
462
    self.assertEqual(master_row[field_index["pinst_list"]],
463
                     (constants.RS_NORMAL,
464
                      list(node_to_primary[master_name])))
465
    self.assertEqual(live_data_row[field_index["sinst_list"]],
466
                     (constants.RS_NORMAL,
467
                      list(node_to_secondary[live_data_name])))
468

    
469
  def testGetLiveNodeField(self):
470
    nodes = [
471
      objects.Node(name="node1", drained=False, offline=False,
472
                   vm_capable=True),
473
      objects.Node(name="node2", drained=True, offline=False,
474
                   vm_capable=True),
475
      objects.Node(name="node3", drained=False, offline=False,
476
                   vm_capable=True),
477
      objects.Node(name="node4", drained=False, offline=True,
478
                   vm_capable=True),
479
      objects.Node(name="node5", drained=False, offline=False,
480
                   vm_capable=False),
481
      ]
482
    live_data = dict.fromkeys([node.name for node in nodes], {})
483

    
484
    # No data
485
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
486
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
487
                                             nqd, nodes[0]),
488
                     query._FS_NODATA)
489

    
490
    # Missing field
491
    ctx = _QueryData(None, curlive_data={
492
      "some": 1,
493
      "other": 2,
494
      })
495
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
496
                                             ctx, nodes[0]),
497
                     query._FS_UNAVAIL)
498

    
499
    # Wrong format/datatype
500
    ctx = _QueryData(None, curlive_data={
501
      "hello": ["Hello World"],
502
      "other": 2,
503
      })
504
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
505
                                             ctx, nodes[0]),
506
                     query._FS_UNAVAIL)
507

    
508
    # Offline node
509
    assert nodes[3].offline
510
    ctx = _QueryData(None, curlive_data={})
511
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
512
                                             ctx, nodes[3]),
513
                     query._FS_OFFLINE, None)
514

    
515
    # Wrong field type
516
    ctx = _QueryData(None, curlive_data={"hello": 123})
517
    self.assertRaises(AssertionError, query._GetLiveNodeField,
518
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
519

    
520
    # Non-vm_capable node
521
    assert not nodes[4].vm_capable
522
    ctx = _QueryData(None, curlive_data={})
523
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
524
                                             ctx, nodes[4]),
525
                     query._FS_UNAVAIL, None)
526

    
527

    
528
class TestInstanceQuery(unittest.TestCase):
529
  def _Create(self, selected):
530
    return query.Query(query.INSTANCE_FIELDS, selected)
531

    
532
  def testSimple(self):
533
    q = self._Create(["name", "be/memory", "ip"])
534
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
535

    
536
    cluster = objects.Cluster(cluster_name="testcluster",
537
      hvparams=constants.HVC_DEFAULTS,
538
      beparams={
539
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
540
        },
541
      nicparams={
542
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
543
        })
544

    
545
    instances = [
546
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
547
      objects.Instance(name="inst2", hvparams={}, nics=[],
548
        beparams={
549
          constants.BE_MEMORY: 512,
550
        }),
551
      objects.Instance(name="inst3", hvparams={}, beparams={},
552
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
553
      ]
554

    
555
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
556
                                  set(), {})
557
    self.assertEqual(q.Query(iqd),
558
      [[(constants.RS_NORMAL, "inst1"),
559
        (constants.RS_NORMAL, 128),
560
        (constants.RS_UNAVAIL, None),
561
       ],
562
       [(constants.RS_NORMAL, "inst2"),
563
        (constants.RS_NORMAL, 512),
564
        (constants.RS_UNAVAIL, None),
565
       ],
566
       [(constants.RS_NORMAL, "inst3"),
567
        (constants.RS_NORMAL, 128),
568
        (constants.RS_NORMAL, "192.0.2.99"),
569
       ]])
570
    self.assertEqual(q.OldStyleQuery(iqd),
571
      [["inst1", 128, None],
572
       ["inst2", 512, None],
573
       ["inst3", 128, "192.0.2.99"]])
574

    
575
  def test(self):
576
    selected = query.INSTANCE_FIELDS.keys()
577
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
578

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

    
582
    q = self._Create(selected)
583
    self.assertEqual(q.RequestedData(),
584
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
585
                          query.IQ_CONSOLE]))
586

    
587
    cluster = objects.Cluster(cluster_name="testcluster",
588
      hvparams=constants.HVC_DEFAULTS,
589
      beparams={
590
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
591
        },
592
      nicparams={
593
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
594
        },
595
      os_hvp={},
596
      tcpudp_port_pool=set())
597

    
598
    offline_nodes = ["nodeoff1", "nodeoff2"]
599
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
600
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
601

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

    
687
    assert not utils.FindDuplicates(inst.name for inst in instances)
688

    
689
    instbyname = dict((inst.name, inst) for inst in instances)
690

    
691
    disk_usage = dict((inst.name,
692
                       cmdlib._ComputeDiskSize(inst.disk_template,
693
                                               [{"size": disk.size}
694
                                                for disk in inst.disks]))
695
                      for inst in instances)
696

    
697
    inst_bridges = {
698
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
699
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
700
                None, "eth123"],
701
      }
702

    
703
    live_data = {
704
      "inst2": {
705
        "vcpus": 3,
706
        },
707
      "inst4": {
708
        "memory": 123,
709
        },
710
      "inst6": {
711
        "memory": 768,
712
        },
713
      }
714
    wrongnode_inst = set("inst2")
715

    
716
    consinfo = dict((inst.name, None) for inst in instances)
717
    consinfo["inst7"] = \
718
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
719
                              host=instbyname["inst7"].primary_node,
720
                              user=constants.GANETI_RUNAS,
721
                              command=["hostname"]).ToDict()
722

    
723
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
724
                                  offline_nodes, bad_nodes, live_data,
725
                                  wrongnode_inst, consinfo)
726
    result = q.Query(iqd)
727
    self.assertEqual(len(result), len(instances))
728
    self.assert_(compat.all(len(row) == len(selected)
729
                            for row in result))
730

    
731
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
732
           "Offline nodes not included in bad nodes"
733

    
734
    tested_status = set()
735

    
736
    for (inst, row) in zip(instances, result):
737
      assert inst.primary_node in nodes
738

    
739
      self.assertEqual(row[fieldidx["name"]],
740
                       (constants.RS_NORMAL, inst.name))
741

    
742
      if inst.primary_node in offline_nodes:
743
        exp_status = "ERROR_nodeoffline"
744
      elif inst.primary_node in bad_nodes:
745
        exp_status = "ERROR_nodedown"
746
      elif inst.name in live_data:
747
        if inst.name in wrongnode_inst:
748
          exp_status = "ERROR_wrongnode"
749
        elif inst.admin_up:
750
          exp_status = "running"
751
        else:
752
          exp_status = "ERROR_up"
753
      elif inst.admin_up:
754
        exp_status = "ERROR_down"
755
      else:
756
        exp_status = "ADMIN_down"
757

    
758
      self.assertEqual(row[fieldidx["status"]],
759
                       (constants.RS_NORMAL, exp_status))
760

    
761
      (_, status) = row[fieldidx["status"]]
762
      tested_status.add(status)
763

    
764
      for (field, livefield) in [("oper_ram", "memory"),
765
                                 ("oper_vcpus", "vcpus")]:
766
        if inst.primary_node in bad_nodes:
767
          exp = (constants.RS_NODATA, None)
768
        elif inst.name in live_data:
769
          value = live_data[inst.name].get(livefield, None)
770
          if value is None:
771
            exp = (constants.RS_UNAVAIL, None)
772
          else:
773
            exp = (constants.RS_NORMAL, value)
774
        else:
775
          exp = (constants.RS_UNAVAIL, None)
776

    
777
        self.assertEqual(row[fieldidx[field]], exp)
778

    
779
      bridges = inst_bridges.get(inst.name, [])
780
      self.assertEqual(row[fieldidx["nic.bridges"]],
781
                       (constants.RS_NORMAL, bridges))
782
      if bridges:
783
        self.assertEqual(row[fieldidx["bridge"]],
784
                         (constants.RS_NORMAL, bridges[0]))
785
      else:
786
        self.assertEqual(row[fieldidx["bridge"]],
787
                         (constants.RS_UNAVAIL, None))
788

    
789
      for i in range(constants.MAX_NICS):
790
        if i < len(bridges) and bridges[i] is not None:
791
          exp = (constants.RS_NORMAL, bridges[i])
792
        else:
793
          exp = (constants.RS_UNAVAIL, None)
794
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
795

    
796
      if inst.primary_node in bad_nodes:
797
        exp = (constants.RS_NODATA, None)
798
      else:
799
        exp = (constants.RS_NORMAL, inst.name in live_data)
800
      self.assertEqual(row[fieldidx["oper_state"]], exp)
801

    
802
      usage = disk_usage[inst.name]
803
      if usage is None:
804
        usage = 0
805
      self.assertEqual(row[fieldidx["disk_usage"]],
806
                       (constants.RS_NORMAL, usage))
807

    
808
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
809
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
810

    
811
      for field in ["ctime", "mtime"]:
812
        if getattr(inst, field) is None:
813
          # No ctime/mtime
814
          exp = (constants.RS_UNAVAIL, None)
815
        else:
816
          exp = (constants.RS_NORMAL, getattr(inst, field))
817
        self.assertEqual(row[fieldidx[field]], exp)
818

    
819
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
820

    
821
    # Ensure all possible status' have been tested
822
    self.assertEqual(tested_status,
823
                     set(["ERROR_nodeoffline", "ERROR_nodedown",
824
                          "running", "ERROR_up", "ERROR_down",
825
                          "ADMIN_down"]))
826

    
827
  def _CheckInstanceConsole(self, instance, (status, consdata)):
828
    if instance.name == "inst7":
829
      self.assertEqual(status, constants.RS_NORMAL)
830
      console = objects.InstanceConsole.FromDict(consdata)
831
      self.assertTrue(console.Validate())
832
      self.assertEqual(console.host, instance.primary_node)
833
    else:
834
      self.assertEqual(status, constants.RS_UNAVAIL)
835

    
836

    
837
class TestGroupQuery(unittest.TestCase):
838

    
839
  def setUp(self):
840
    self.groups = [
841
      objects.NodeGroup(name="default",
842
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
843
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
844
      objects.NodeGroup(name="restricted",
845
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
846
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
847
      ]
848

    
849
  def _Create(self, selected):
850
    return query.Query(query.GROUP_FIELDS, selected)
851

    
852
  def testSimple(self):
853
    q = self._Create(["name", "uuid", "alloc_policy"])
854
    gqd = query.GroupQueryData(self.groups, None, None)
855

    
856
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
857

    
858
    self.assertEqual(q.Query(gqd),
859
      [[(constants.RS_NORMAL, "default"),
860
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
861
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
862
        ],
863
       [(constants.RS_NORMAL, "restricted"),
864
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
865
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
866
        ],
867
       ])
868

    
869
  def testNodes(self):
870
    groups_to_nodes = {
871
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
872
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
873
      }
874

    
875
    q = self._Create(["name", "node_cnt", "node_list"])
876
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
877

    
878
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
879

    
880
    self.assertEqual(q.Query(gqd),
881
                     [[(constants.RS_NORMAL, "default"),
882
                       (constants.RS_NORMAL, 2),
883
                       (constants.RS_NORMAL, ["node1", "node2"]),
884
                       ],
885
                      [(constants.RS_NORMAL, "restricted"),
886
                       (constants.RS_NORMAL, 3),
887
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
888
                       ],
889
                      ])
890

    
891
  def testInstances(self):
892
    groups_to_instances = {
893
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
894
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
895
      }
896

    
897
    q = self._Create(["pinst_cnt", "pinst_list"])
898
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
899

    
900
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
901

    
902
    self.assertEqual(q.Query(gqd),
903
                     [[(constants.RS_NORMAL, 2),
904
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
905
                       ],
906
                      [(constants.RS_NORMAL, 3),
907
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
908
                       ],
909
                      ])
910

    
911

    
912
class TestQueryFields(unittest.TestCase):
913
  def testAllFields(self):
914
    for fielddefs in query.ALL_FIELD_LISTS:
915
      result = query.QueryFields(fielddefs, None)
916
      self.assert_(isinstance(result, dict))
917
      response = objects.QueryFieldsResponse.FromDict(result)
918
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
919
        [(fdef2.name, fdef2.title)
920
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
921
                                             key=lambda x: x[0].name)])
922

    
923
  def testSomeFields(self):
924
    rnd = random.Random(5357)
925

    
926
    for _ in range(10):
927
      for fielddefs in query.ALL_FIELD_LISTS:
928
        if len(fielddefs) > 20:
929
          sample_size = rnd.randint(5, 20)
930
        else:
931
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
932
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
933
                                                      sample_size)]
934
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
935
        self.assert_(isinstance(result, dict))
936
        response = objects.QueryFieldsResponse.FromDict(result)
937
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
938
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
939

    
940

    
941
if __name__ == "__main__":
942
  testutils.GanetiTestProgram()