Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 52b5d286

History | View | Annotate | Download (32.8 kB)

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

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

    
21

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

    
24
import re
25
import unittest
26
import random
27

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

    
36
import testutils
37

    
38

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

    
44

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

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

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

    
55

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

    
63

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
258

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

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

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

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

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

    
280

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

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

    
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, query.NQ_OOB]))
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
    oob_support = dict((name, False) for name in node_names)
380

    
381
    master_node.group = ng_uuid
382

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

    
391
    node_to_row = dict((row[field_index["name"]][1], idx)
392
                       for idx, row in enumerate(result))
393

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

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

    
421
    live_data_row = result[node_to_row[live_data_name]]
422

    
423
    for (field, value) in fake_live_data.items():
424
      self.assertEqual(live_data_row[field_index[field]],
425
                       (constants.QRFS_NORMAL, value))
426

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

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

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

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

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

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

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

    
483

    
484
class TestInstanceQuery(unittest.TestCase):
485
  def _Create(self, selected):
486
    return query.Query(query.INSTANCE_FIELDS, selected)
487

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

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

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

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

    
530
  def test(self):
531
    selected = query.INSTANCE_FIELDS.keys()
532
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
533

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

    
537
    q = self._Create(selected)
538
    self.assertEqual(q.RequestedData(),
539
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
540

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

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

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

    
641
    assert not utils.FindDuplicates(inst.name for inst in instances)
642

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

    
649
    inst_bridges = {
650
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
651
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
652
                None, "eth123"],
653
      }
654

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

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

    
674
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
675
           "Offline nodes not included in bad nodes"
676

    
677
    tested_status = set()
678

    
679
    for (inst, row) in zip(instances, result):
680
      assert inst.primary_node in nodes
681

    
682
      self.assertEqual(row[fieldidx["name"]],
683
                       (constants.QRFS_NORMAL, inst.name))
684

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

    
699
      self.assertEqual(row[fieldidx["status"]],
700
                       (constants.QRFS_NORMAL, exp_status))
701

    
702
      (_, status) = row[fieldidx["status"]]
703
      tested_status.add(status)
704

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

    
718
        self.assertEqual(row[fieldidx[field]], exp)
719

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

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

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

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

    
749
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
750
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
751

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

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

    
766

    
767
class TestGroupQuery(unittest.TestCase):
768

    
769
  def setUp(self):
770
    self.groups = [
771
      objects.NodeGroup(name="default",
772
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
773
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
774
      objects.NodeGroup(name="restricted",
775
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
776
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
777
      ]
778

    
779
  def _Create(self, selected):
780
    return query.Query(query.GROUP_FIELDS, selected)
781

    
782
  def testSimple(self):
783
    q = self._Create(["name", "uuid", "alloc_policy"])
784
    gqd = query.GroupQueryData(self.groups, None, None)
785

    
786
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
787

    
788
    self.assertEqual(q.Query(gqd),
789
      [[(constants.QRFS_NORMAL, "default"),
790
        (constants.QRFS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
791
        (constants.QRFS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
792
        ],
793
       [(constants.QRFS_NORMAL, "restricted"),
794
        (constants.QRFS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
795
        (constants.QRFS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
796
        ],
797
       ])
798

    
799
  def testNodes(self):
800
    groups_to_nodes = {
801
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
802
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
803
      }
804

    
805
    q = self._Create(["name", "node_cnt", "node_list"])
806
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
807

    
808
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
809

    
810
    self.assertEqual(q.Query(gqd),
811
                     [[(constants.QRFS_NORMAL, "default"),
812
                       (constants.QRFS_NORMAL, 2),
813
                       (constants.QRFS_NORMAL, ["node1", "node2"]),
814
                       ],
815
                      [(constants.QRFS_NORMAL, "restricted"),
816
                       (constants.QRFS_NORMAL, 3),
817
                       (constants.QRFS_NORMAL, ["node1", "node9", "node10"]),
818
                       ],
819
                      ])
820

    
821
  def testInstances(self):
822
    groups_to_instances = {
823
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
824
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
825
      }
826

    
827
    q = self._Create(["pinst_cnt", "pinst_list"])
828
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
829

    
830
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
831

    
832
    self.assertEqual(q.Query(gqd),
833
                     [[(constants.QRFS_NORMAL, 2),
834
                       (constants.QRFS_NORMAL, ["inst1", "inst2"]),
835
                       ],
836
                      [(constants.QRFS_NORMAL, 3),
837
                       (constants.QRFS_NORMAL, ["inst1", "inst9", "inst10"]),
838
                       ],
839
                      ])
840

    
841

    
842
class TestQueryFields(unittest.TestCase):
843
  def testAllFields(self):
844
    for fielddefs in query.ALL_FIELD_LISTS:
845
      result = query.QueryFields(fielddefs, None)
846
      self.assert_(isinstance(result, dict))
847
      response = objects.QueryFieldsResponse.FromDict(result)
848
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
849
        [(fdef2.name, fdef2.title)
850
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
851
                                             key=lambda x: x[0].name)])
852

    
853
  def testSomeFields(self):
854
    rnd = random.Random(5357)
855

    
856
    for _ in range(10):
857
      for fielddefs in query.ALL_FIELD_LISTS:
858
        if len(fielddefs) > 20:
859
          sample_size = rnd.randint(5, 20)
860
        else:
861
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
862
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
863
                                                      sample_size)]
864
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
865
        self.assert_(isinstance(result, dict))
866
        response = objects.QueryFieldsResponse.FromDict(result)
867
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
868
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
869

    
870

    
871
if __name__ == "__main__":
872
  testutils.GanetiTestProgram()