Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ e431074f

History | View | Annotate | Download (33.8 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=False,
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
      objects.Node(name="node2", drained=True, offline=False),
473
      objects.Node(name="node3", drained=False, offline=False),
474
      objects.Node(name="node4", drained=False, offline=True),
475
      ]
476
    live_data = dict.fromkeys([node.name for node in nodes], {})
477

    
478
    # No data
479
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
480
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
481
                                             nqd, nodes[0]),
482
                     query._FS_NODATA)
483

    
484
    # Missing field
485
    ctx = _QueryData(None, curlive_data={
486
      "some": 1,
487
      "other": 2,
488
      })
489
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
490
                                             ctx, nodes[0]),
491
                     query._FS_UNAVAIL)
492

    
493
    # Wrong format/datatype
494
    ctx = _QueryData(None, curlive_data={
495
      "hello": ["Hello World"],
496
      "other": 2,
497
      })
498
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
499
                                             ctx, nodes[0]),
500
                     query._FS_UNAVAIL)
501

    
502
    # Offline node
503
    assert nodes[3].offline
504
    ctx = _QueryData(None, curlive_data={})
505
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
506
                                             ctx, nodes[3]),
507
                     query._FS_OFFLINE, None)
508

    
509
    # Wrong field type
510
    ctx = _QueryData(None, curlive_data={"hello": 123})
511
    self.assertRaises(AssertionError, query._GetLiveNodeField,
512
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
513

    
514

    
515
class TestInstanceQuery(unittest.TestCase):
516
  def _Create(self, selected):
517
    return query.Query(query.INSTANCE_FIELDS, selected)
518

    
519
  def testSimple(self):
520
    q = self._Create(["name", "be/memory", "ip"])
521
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
522

    
523
    cluster = objects.Cluster(cluster_name="testcluster",
524
      hvparams=constants.HVC_DEFAULTS,
525
      beparams={
526
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
527
        },
528
      nicparams={
529
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
530
        })
531

    
532
    instances = [
533
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
534
      objects.Instance(name="inst2", hvparams={}, nics=[],
535
        beparams={
536
          constants.BE_MEMORY: 512,
537
        }),
538
      objects.Instance(name="inst3", hvparams={}, beparams={},
539
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
540
      ]
541

    
542
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {}, set())
543
    self.assertEqual(q.Query(iqd),
544
      [[(constants.RS_NORMAL, "inst1"),
545
        (constants.RS_NORMAL, 128),
546
        (constants.RS_UNAVAIL, None),
547
       ],
548
       [(constants.RS_NORMAL, "inst2"),
549
        (constants.RS_NORMAL, 512),
550
        (constants.RS_UNAVAIL, None),
551
       ],
552
       [(constants.RS_NORMAL, "inst3"),
553
        (constants.RS_NORMAL, 128),
554
        (constants.RS_NORMAL, "192.0.2.99"),
555
       ]])
556
    self.assertEqual(q.OldStyleQuery(iqd),
557
      [["inst1", 128, None],
558
       ["inst2", 512, None],
559
       ["inst3", 128, "192.0.2.99"]])
560

    
561
  def test(self):
562
    selected = query.INSTANCE_FIELDS.keys()
563
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
564

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

    
568
    q = self._Create(selected)
569
    self.assertEqual(q.RequestedData(),
570
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
571

    
572
    cluster = objects.Cluster(cluster_name="testcluster",
573
      hvparams=constants.HVC_DEFAULTS,
574
      beparams={
575
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
576
        },
577
      nicparams={
578
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
579
        },
580
      os_hvp={},
581
      tcpudp_port_pool=set())
582

    
583
    offline_nodes = ["nodeoff1", "nodeoff2"]
584
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
585
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
586

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

    
672
    assert not utils.FindDuplicates(inst.name for inst in instances)
673

    
674
    disk_usage = dict((inst.name,
675
                       cmdlib._ComputeDiskSize(inst.disk_template,
676
                                               [{"size": disk.size}
677
                                                for disk in inst.disks]))
678
                      for inst in instances)
679

    
680
    inst_bridges = {
681
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
682
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
683
                None, "eth123"],
684
      }
685

    
686
    live_data = {
687
      "inst2": {
688
        "vcpus": 3,
689
        },
690
      "inst4": {
691
        "memory": 123,
692
        },
693
      "inst6": {
694
        "memory": 768,
695
        },
696
      }
697
    wrongnode_inst = set("inst2")
698

    
699
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
700
                                  offline_nodes, bad_nodes, live_data,
701
                                  wrongnode_inst)
702
    result = q.Query(iqd)
703
    self.assertEqual(len(result), len(instances))
704
    self.assert_(compat.all(len(row) == len(selected)
705
                            for row in result))
706

    
707
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
708
           "Offline nodes not included in bad nodes"
709

    
710
    tested_status = set()
711

    
712
    for (inst, row) in zip(instances, result):
713
      assert inst.primary_node in nodes
714

    
715
      self.assertEqual(row[fieldidx["name"]],
716
                       (constants.RS_NORMAL, inst.name))
717

    
718
      if inst.primary_node in offline_nodes:
719
        exp_status = "ERROR_nodeoffline"
720
      elif inst.primary_node in bad_nodes:
721
        exp_status = "ERROR_nodedown"
722
      elif inst.name in live_data:
723
        if inst.name in wrongnode_inst:
724
          exp_status = "ERROR_wrongnode"
725
        elif inst.admin_up:
726
          exp_status = "running"
727
        else:
728
          exp_status = "ERROR_up"
729
      elif inst.admin_up:
730
        exp_status = "ERROR_down"
731
      else:
732
        exp_status = "ADMIN_down"
733

    
734
      self.assertEqual(row[fieldidx["status"]],
735
                       (constants.RS_NORMAL, exp_status))
736

    
737
      (_, status) = row[fieldidx["status"]]
738
      tested_status.add(status)
739

    
740
      for (field, livefield) in [("oper_ram", "memory"),
741
                                 ("oper_vcpus", "vcpus")]:
742
        if inst.primary_node in bad_nodes:
743
          exp = (constants.RS_NODATA, None)
744
        elif inst.name in live_data:
745
          value = live_data[inst.name].get(livefield, None)
746
          if value is None:
747
            exp = (constants.RS_UNAVAIL, None)
748
          else:
749
            exp = (constants.RS_NORMAL, value)
750
        else:
751
          exp = (constants.RS_UNAVAIL, None)
752

    
753
        self.assertEqual(row[fieldidx[field]], exp)
754

    
755
      bridges = inst_bridges.get(inst.name, [])
756
      self.assertEqual(row[fieldidx["nic.bridges"]],
757
                       (constants.RS_NORMAL, bridges))
758
      if bridges:
759
        self.assertEqual(row[fieldidx["bridge"]],
760
                         (constants.RS_NORMAL, bridges[0]))
761
      else:
762
        self.assertEqual(row[fieldidx["bridge"]],
763
                         (constants.RS_UNAVAIL, None))
764

    
765
      for i in range(constants.MAX_NICS):
766
        if i < len(bridges) and bridges[i] is not None:
767
          exp = (constants.RS_NORMAL, bridges[i])
768
        else:
769
          exp = (constants.RS_UNAVAIL, None)
770
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
771

    
772
      if inst.primary_node in bad_nodes:
773
        exp = (constants.RS_NODATA, None)
774
      else:
775
        exp = (constants.RS_NORMAL, inst.name in live_data)
776
      self.assertEqual(row[fieldidx["oper_state"]], exp)
777

    
778
      usage = disk_usage[inst.name]
779
      if usage is None:
780
        usage = 0
781
      self.assertEqual(row[fieldidx["disk_usage"]],
782
                       (constants.RS_NORMAL, usage))
783

    
784
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
785
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
786

    
787
      for field in ["ctime", "mtime"]:
788
        if getattr(inst, field) is None:
789
          # No ctime/mtime
790
          exp = (constants.RS_UNAVAIL, None)
791
        else:
792
          exp = (constants.RS_NORMAL, getattr(inst, field))
793
        self.assertEqual(row[fieldidx[field]], exp)
794

    
795
    # Ensure all possible status' have been tested
796
    self.assertEqual(tested_status,
797
                     set(["ERROR_nodeoffline", "ERROR_nodedown",
798
                          "running", "ERROR_up", "ERROR_down",
799
                          "ADMIN_down"]))
800

    
801

    
802
class TestGroupQuery(unittest.TestCase):
803

    
804
  def setUp(self):
805
    self.groups = [
806
      objects.NodeGroup(name="default",
807
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
808
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
809
      objects.NodeGroup(name="restricted",
810
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
811
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
812
      ]
813

    
814
  def _Create(self, selected):
815
    return query.Query(query.GROUP_FIELDS, selected)
816

    
817
  def testSimple(self):
818
    q = self._Create(["name", "uuid", "alloc_policy"])
819
    gqd = query.GroupQueryData(self.groups, None, None)
820

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

    
823
    self.assertEqual(q.Query(gqd),
824
      [[(constants.RS_NORMAL, "default"),
825
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
826
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
827
        ],
828
       [(constants.RS_NORMAL, "restricted"),
829
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
830
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
831
        ],
832
       ])
833

    
834
  def testNodes(self):
835
    groups_to_nodes = {
836
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
837
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
838
      }
839

    
840
    q = self._Create(["name", "node_cnt", "node_list"])
841
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
842

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

    
845
    self.assertEqual(q.Query(gqd),
846
                     [[(constants.RS_NORMAL, "default"),
847
                       (constants.RS_NORMAL, 2),
848
                       (constants.RS_NORMAL, ["node1", "node2"]),
849
                       ],
850
                      [(constants.RS_NORMAL, "restricted"),
851
                       (constants.RS_NORMAL, 3),
852
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
853
                       ],
854
                      ])
855

    
856
  def testInstances(self):
857
    groups_to_instances = {
858
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
859
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
860
      }
861

    
862
    q = self._Create(["pinst_cnt", "pinst_list"])
863
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
864

    
865
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
866

    
867
    self.assertEqual(q.Query(gqd),
868
                     [[(constants.RS_NORMAL, 2),
869
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
870
                       ],
871
                      [(constants.RS_NORMAL, 3),
872
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
873
                       ],
874
                      ])
875

    
876

    
877
class TestQueryFields(unittest.TestCase):
878
  def testAllFields(self):
879
    for fielddefs in query.ALL_FIELD_LISTS:
880
      result = query.QueryFields(fielddefs, None)
881
      self.assert_(isinstance(result, dict))
882
      response = objects.QueryFieldsResponse.FromDict(result)
883
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
884
        [(fdef2.name, fdef2.title)
885
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
886
                                             key=lambda x: x[0].name)])
887

    
888
  def testSomeFields(self):
889
    rnd = random.Random(5357)
890

    
891
    for _ in range(10):
892
      for fielddefs in query.ALL_FIELD_LISTS:
893
        if len(fielddefs) > 20:
894
          sample_size = rnd.randint(5, 20)
895
        else:
896
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
897
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
898
                                                      sample_size)]
899
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
900
        self.assert_(isinstance(result, dict))
901
        response = objects.QueryFieldsResponse.FromDict(result)
902
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
903
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
904

    
905

    
906
if __name__ == "__main__":
907
  testutils.GanetiTestProgram()