Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ b12d5e2e

History | View | Annotate | Download (36.9 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, "Name"),
70
       STATIC, lambda ctx, item: item["name"]),
71
      (query._MakeField("master", "Master", constants.QFT_BOOL, "Master"),
72
       STATIC, lambda ctx, item: ctx.mastername == item["name"]),
73
      ] +
74
      [(query._MakeField("disk%s.size" % i, "DiskSize%s" % i,
75
                         constants.QFT_UNIT, "Disk size %s" % i),
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,
87
                                   doc="Name").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.RS_NORMAL, "node1")],
98
                      [(constants.RS_NORMAL, "node2")],
99
                      [(constants.RS_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.RS_NORMAL, "node1"),
108
                       (constants.RS_NORMAL, False)],
109
                      [(constants.RS_NORMAL, "node2"),
110
                       (constants.RS_NORMAL, False)],
111
                      [(constants.RS_NORMAL, "node3"),
112
                       (constants.RS_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.RS_NORMAL, "node1"),
120
                       (constants.RS_NORMAL, False),
121
                       (constants.RS_NORMAL, 0)],
122
                      [(constants.RS_NORMAL, "node2"),
123
                       (constants.RS_NORMAL, True),
124
                       (constants.RS_NORMAL, 3)],
125
                      [(constants.RS_NORMAL, "node3"),
126
                       (constants.RS_NORMAL, False),
127
                       (constants.RS_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.RS_NORMAL, 2),
137
                       (constants.RS_NORMAL, 1),
138
                       (constants.RS_UNKNOWN, None),
139
                       (constants.RS_NORMAL, 0)],
140
                      [(constants.RS_UNAVAIL, None),
141
                       (constants.RS_NORMAL, 4),
142
                       (constants.RS_UNKNOWN, None),
143
                       (constants.RS_NORMAL, 3)],
144
                      [(constants.RS_NORMAL, 7),
145
                       (constants.RS_NORMAL, 6),
146
                       (constants.RS_UNKNOWN, None),
147
                       (constants.RS_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, "doc": "Disk size 2", },
154
                     { "name": "disk1.size", "title": "DiskSize1",
155
                       "kind": constants.QFT_UNIT, "doc": "Disk size 1", },
156
                     { "name": "disk99.size", "title": "disk99.size",
157
                       "kind": constants.QFT_UNKNOWN,
158
                       "doc": "Unknown field 'disk99.size'", },
159
                     { "name": "disk0.size", "title": "DiskSize0",
160
                       "kind": constants.QFT_UNIT, "doc": "Disk size 0", },
161
                     ])
162

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

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

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

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

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

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

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

    
217
    # Invalid documentation
218
    for doc in ["", ".", "Hello world\n", "Hello\nWo\nrld", "Hello World!",
219
                "HelloWorld.", "only lowercase", ",", " x y z .\t", "  "]:
220
      self.assertRaises(AssertionError, query._PrepareFieldList, [
221
        (query._MakeField("name", "Name", constants.QFT_TEXT, doc),
222
        None, lambda *args: None),
223
        ], [])
224

    
225
  def testUnknown(self):
226
    fielddef = query._PrepareFieldList([
227
      (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
228
       None, lambda _, item: "name%s" % item),
229
      (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP, "Other"),
230
       None, lambda *args: 1234),
231
      (query._MakeField("nodata", "NoData", constants.QFT_NUMBER, "No data"),
232
       None, lambda *args: query._FS_NODATA ),
233
      (query._MakeField("unavail", "Unavail", constants.QFT_BOOL, "Unavail"),
234
       None, lambda *args: query._FS_UNAVAIL),
235
      ], [])
236

    
237
    for selected in [["foo"], ["Hello", "World"],
238
                     ["name1", "other", "foo"]]:
239
      q = query.Query(fielddef, selected)
240
      self.assertEqual(len(q._fields), len(selected))
241
      self.assert_(compat.all(len(row) == len(selected)
242
                              for row in q.Query(_QueryData(range(1, 10)))))
243
      self.assertEqual(q.Query(_QueryData(range(1, 10))),
244
                       [[(constants.RS_UNKNOWN, None)] * len(selected)
245
                        for i in range(1, 10)])
246
      self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
247
                       [{ "name": name, "title": name,
248
                          "kind": constants.QFT_UNKNOWN,
249
                          "doc": "Unknown field '%s'" % name}
250
                        for name in selected])
251

    
252
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
253
    self.assertEqual(len(q._fields), 4)
254
    self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
255
                     ["name%s" % i, 1234, None, None]
256
                     for i in range(1, 10)
257
                     ])
258

    
259
    q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
260
    self.assertEqual(len(q._fields), 5)
261
    self.assertEqual(q.Query(_QueryData(range(1, 10))),
262
                     [[(constants.RS_NORMAL, "name%s" % i),
263
                       (constants.RS_NORMAL, 1234),
264
                       (constants.RS_NODATA, None),
265
                       (constants.RS_UNAVAIL, None),
266
                       (constants.RS_UNKNOWN, None)]
267
                      for i in range(1, 10)])
268

    
269
  def testAliases(self):
270
    fields = [
271
      (query._MakeField("a", "a-title", constants.QFT_TEXT, "Field A"), None,
272
       lambda *args: None),
273
      (query._MakeField("b", "b-title", constants.QFT_TEXT, "Field B"), None,
274
       lambda *args: None),
275
      ]
276
    # duplicate field
277
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
278
                      [("b", "a")])
279
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
280
                      [("c", "b"), ("c", "a")])
281
    # missing target
282
    self.assertRaises(AssertionError, query._PrepareFieldList, fields,
283
                      [("c", "d")])
284
    fdefs = query._PrepareFieldList(fields, [("c", "b")])
285
    self.assertEqual(len(fdefs), 3)
286
    self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
287

    
288

    
289
class TestGetNodeRole(unittest.TestCase):
290
  def test(self):
291
    tested_role = set()
292

    
293
    checks = [
294
      (constants.NR_MASTER, "node1", objects.Node(name="node1")),
295
      (constants.NR_MCANDIDATE, "master",
296
       objects.Node(name="node1", master_candidate=True)),
297
      (constants.NR_REGULAR, "master", objects.Node(name="node1")),
298
      (constants.NR_DRAINED, "master",
299
       objects.Node(name="node1", drained=True)),
300
      (constants.NR_OFFLINE,
301
       "master", objects.Node(name="node1", offline=True)),
302
      ]
303

    
304
    for (role, master_name, node) in checks:
305
      result = query._GetNodeRole(node, master_name)
306
      self.assertEqual(result, role)
307
      tested_role.add(result)
308

    
309
    self.assertEqual(tested_role, constants.NR_ALL)
310

    
311

    
312
class TestNodeQuery(unittest.TestCase):
313
  def _Create(self, selected):
314
    return query.Query(query.NODE_FIELDS, selected)
315

    
316
  def testSimple(self):
317
    nodes = [
318
      objects.Node(name="node1", drained=False),
319
      objects.Node(name="node2", drained=True),
320
      objects.Node(name="node3", drained=False),
321
      ]
322
    for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
323
      nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None,
324
                                None)
325

    
326
      q = self._Create(["name", "drained"])
327
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
328
      self.assertEqual(q.Query(nqd),
329
                       [[(constants.RS_NORMAL, "node1"),
330
                         (constants.RS_NORMAL, False)],
331
                        [(constants.RS_NORMAL, "node2"),
332
                         (constants.RS_NORMAL, True)],
333
                        [(constants.RS_NORMAL, "node3"),
334
                         (constants.RS_NORMAL, False)],
335
                       ])
336
      self.assertEqual(q.OldStyleQuery(nqd),
337
                       [["node1", False],
338
                        ["node2", True],
339
                        ["node3", False]])
340

    
341
  def test(self):
342
    selected = query.NODE_FIELDS.keys()
343
    field_index = dict((field, idx) for idx, field in enumerate(selected))
344

    
345
    q = self._Create(selected)
346
    self.assertEqual(q.RequestedData(),
347
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
348
                          query.NQ_GROUP, query.NQ_OOB]))
349

    
350
    cluster = objects.Cluster(cluster_name="testcluster",
351
      hvparams=constants.HVC_DEFAULTS,
352
      beparams={
353
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
354
        },
355
      nicparams={
356
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
357
        },
358
      ndparams=constants.NDC_DEFAULTS,
359
        )
360

    
361
    node_names = ["node%s" % i for i in range(20)]
362
    master_name = node_names[3]
363
    nodes = [
364
      objects.Node(name=name,
365
                   primary_ip="192.0.2.%s" % idx,
366
                   secondary_ip="192.0.100.%s" % idx,
367
                   serial_no=7789 * idx,
368
                   master_candidate=(name != master_name and idx % 3 == 0),
369
                   offline=False,
370
                   drained=False,
371
                   powered=True,
372
                   vm_capable=True,
373
                   master_capable=False,
374
                   ndparams={},
375
                   group="default",
376
                   ctime=1290006900,
377
                   mtime=1290006913,
378
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
379
      for idx, name in enumerate(node_names)
380
      ]
381

    
382
    master_node = nodes[3]
383
    master_node.AddTag("masternode")
384
    master_node.AddTag("another")
385
    master_node.AddTag("tag")
386
    master_node.ctime = None
387
    master_node.mtime = None
388
    assert master_node.name == master_name
389

    
390
    live_data_name = node_names[4]
391
    assert live_data_name != master_name
392

    
393
    fake_live_data = {
394
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
395
      "cnodes": 4,
396
      "csockets": 4,
397
      "ctotal": 8,
398
      "mnode": 128,
399
      "mfree": 100,
400
      "mtotal": 4096,
401
      "dfree": 5 * 1024 * 1024,
402
      "dtotal": 100 * 1024 * 1024,
403
      }
404

    
405
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
406
            sorted(fake_live_data.keys()))
407

    
408
    live_data = dict.fromkeys(node_names, {})
409
    live_data[live_data_name] = \
410
      dict((query._NODE_LIVE_FIELDS[name][2], value)
411
           for name, value in fake_live_data.items())
412

    
413
    node_to_primary = dict((name, set()) for name in node_names)
414
    node_to_primary[master_name].update(["inst1", "inst2"])
415

    
416
    node_to_secondary = dict((name, set()) for name in node_names)
417
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
418

    
419
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
420
    groups = {
421
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
422
      }
423

    
424
    oob_not_powered_node = node_names[0]
425
    nodes[0].powered = False
426
    oob_support = dict((name, False) for name in node_names)
427
    oob_support[master_name] = True
428
    oob_support[oob_not_powered_node] = True
429

    
430
    master_node.group = ng_uuid
431

    
432
    nqd = query.NodeQueryData(nodes, live_data, master_name,
433
                              node_to_primary, node_to_secondary, groups,
434
                              oob_support, cluster)
435
    result = q.Query(nqd)
436
    self.assert_(compat.all(len(row) == len(selected) for row in result))
437
    self.assertEqual([row[field_index["name"]] for row in result],
438
                     [(constants.RS_NORMAL, name) for name in node_names])
439

    
440
    node_to_row = dict((row[field_index["name"]][1], idx)
441
                       for idx, row in enumerate(result))
442

    
443
    master_row = result[node_to_row[master_name]]
444
    self.assert_(master_row[field_index["master"]])
445
    self.assert_(master_row[field_index["role"]], "M")
446
    self.assertEqual(master_row[field_index["group"]],
447
                     (constants.RS_NORMAL, "ng1"))
448
    self.assertEqual(master_row[field_index["group.uuid"]],
449
                     (constants.RS_NORMAL, ng_uuid))
450
    self.assertEqual(master_row[field_index["ctime"]],
451
                     (constants.RS_UNAVAIL, None))
452
    self.assertEqual(master_row[field_index["mtime"]],
453
                     (constants.RS_UNAVAIL, None))
454

    
455
    self.assert_(row[field_index["pip"]] == node.primary_ip and
456
                 row[field_index["sip"]] == node.secondary_ip and
457
                 set(row[field_index["tags"]]) == node.GetTags() and
458
                 row[field_index["serial_no"]] == node.serial_no and
459
                 row[field_index["role"]] == query._GetNodeRole(node,
460
                                                                master_name) and
461
                 (node.name == master_name or
462
                  (row[field_index["group"]] == "<unknown>" and
463
                   row[field_index["group.uuid"]] is None and
464
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
465
                                                 node.ctime) and
466
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
467
                                                 node.mtime) and
468
                   row[field_index["powered"]] == (constants.RS_NORMAL,
469
                                                   True))) or
470
                 (node.name == oob_not_powered_node and
471
                  row[field_index["powered"]] == (constants.RS_NORMAL,
472
                                                  False)) or
473
                 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
474
                 for row, node in zip(result, nodes))
475

    
476
    live_data_row = result[node_to_row[live_data_name]]
477

    
478
    for (field, value) in fake_live_data.items():
479
      self.assertEqual(live_data_row[field_index[field]],
480
                       (constants.RS_NORMAL, value))
481

    
482
    self.assertEqual(master_row[field_index["pinst_cnt"]],
483
                     (constants.RS_NORMAL, 2))
484
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
485
                     (constants.RS_NORMAL, 3))
486
    self.assertEqual(master_row[field_index["pinst_list"]],
487
                     (constants.RS_NORMAL,
488
                      list(node_to_primary[master_name])))
489
    self.assertEqual(live_data_row[field_index["sinst_list"]],
490
                     (constants.RS_NORMAL,
491
                      list(node_to_secondary[live_data_name])))
492

    
493
  def testGetLiveNodeField(self):
494
    nodes = [
495
      objects.Node(name="node1", drained=False, offline=False,
496
                   vm_capable=True),
497
      objects.Node(name="node2", drained=True, offline=False,
498
                   vm_capable=True),
499
      objects.Node(name="node3", drained=False, offline=False,
500
                   vm_capable=True),
501
      objects.Node(name="node4", drained=False, offline=True,
502
                   vm_capable=True),
503
      objects.Node(name="node5", drained=False, offline=False,
504
                   vm_capable=False),
505
      ]
506
    live_data = dict.fromkeys([node.name for node in nodes], {})
507

    
508
    # No data
509
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
510
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
511
                                             nqd, nodes[0]),
512
                     query._FS_NODATA)
513

    
514
    # Missing field
515
    ctx = _QueryData(None, curlive_data={
516
      "some": 1,
517
      "other": 2,
518
      })
519
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
520
                                             ctx, nodes[0]),
521
                     query._FS_UNAVAIL)
522

    
523
    # Wrong format/datatype
524
    ctx = _QueryData(None, curlive_data={
525
      "hello": ["Hello World"],
526
      "other": 2,
527
      })
528
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
529
                                             ctx, nodes[0]),
530
                     query._FS_UNAVAIL)
531

    
532
    # Offline node
533
    assert nodes[3].offline
534
    ctx = _QueryData(None, curlive_data={})
535
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
536
                                             ctx, nodes[3]),
537
                     query._FS_OFFLINE, None)
538

    
539
    # Wrong field type
540
    ctx = _QueryData(None, curlive_data={"hello": 123})
541
    self.assertRaises(AssertionError, query._GetLiveNodeField,
542
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
543

    
544
    # Non-vm_capable node
545
    assert not nodes[4].vm_capable
546
    ctx = _QueryData(None, curlive_data={})
547
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
548
                                             ctx, nodes[4]),
549
                     query._FS_UNAVAIL, None)
550

    
551

    
552
class TestInstanceQuery(unittest.TestCase):
553
  def _Create(self, selected):
554
    return query.Query(query.INSTANCE_FIELDS, selected)
555

    
556
  def testSimple(self):
557
    q = self._Create(["name", "be/memory", "ip"])
558
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
559

    
560
    cluster = objects.Cluster(cluster_name="testcluster",
561
      hvparams=constants.HVC_DEFAULTS,
562
      beparams={
563
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
564
        },
565
      nicparams={
566
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
567
        })
568

    
569
    instances = [
570
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
571
      objects.Instance(name="inst2", hvparams={}, nics=[],
572
        beparams={
573
          constants.BE_MEMORY: 512,
574
        }),
575
      objects.Instance(name="inst3", hvparams={}, beparams={},
576
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
577
      ]
578

    
579
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
580
                                  set(), {})
581
    self.assertEqual(q.Query(iqd),
582
      [[(constants.RS_NORMAL, "inst1"),
583
        (constants.RS_NORMAL, 128),
584
        (constants.RS_UNAVAIL, None),
585
       ],
586
       [(constants.RS_NORMAL, "inst2"),
587
        (constants.RS_NORMAL, 512),
588
        (constants.RS_UNAVAIL, None),
589
       ],
590
       [(constants.RS_NORMAL, "inst3"),
591
        (constants.RS_NORMAL, 128),
592
        (constants.RS_NORMAL, "192.0.2.99"),
593
       ]])
594
    self.assertEqual(q.OldStyleQuery(iqd),
595
      [["inst1", 128, None],
596
       ["inst2", 512, None],
597
       ["inst3", 128, "192.0.2.99"]])
598

    
599
  def test(self):
600
    selected = query.INSTANCE_FIELDS.keys()
601
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
602

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

    
606
    q = self._Create(selected)
607
    self.assertEqual(q.RequestedData(),
608
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
609
                          query.IQ_CONSOLE]))
610

    
611
    cluster = objects.Cluster(cluster_name="testcluster",
612
      hvparams=constants.HVC_DEFAULTS,
613
      beparams={
614
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
615
        },
616
      nicparams={
617
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
618
        },
619
      os_hvp={},
620
      tcpudp_port_pool=set())
621

    
622
    offline_nodes = ["nodeoff1", "nodeoff2"]
623
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
624
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
625

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

    
711
    assert not utils.FindDuplicates(inst.name for inst in instances)
712

    
713
    instbyname = dict((inst.name, inst) for inst in instances)
714

    
715
    disk_usage = dict((inst.name,
716
                       cmdlib._ComputeDiskSize(inst.disk_template,
717
                                               [{"size": disk.size}
718
                                                for disk in inst.disks]))
719
                      for inst in instances)
720

    
721
    inst_bridges = {
722
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
723
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
724
                None, "eth123"],
725
      }
726

    
727
    live_data = {
728
      "inst2": {
729
        "vcpus": 3,
730
        },
731
      "inst4": {
732
        "memory": 123,
733
        },
734
      "inst6": {
735
        "memory": 768,
736
        },
737
      "inst7": {
738
        "vcpus": 3,
739
        },
740
      }
741
    wrongnode_inst = set(["inst7"])
742

    
743
    consinfo = dict((inst.name, None) for inst in instances)
744
    consinfo["inst7"] = \
745
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
746
                              host=instbyname["inst7"].primary_node,
747
                              user=constants.GANETI_RUNAS,
748
                              command=["hostname"]).ToDict()
749

    
750
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
751
                                  offline_nodes, bad_nodes, live_data,
752
                                  wrongnode_inst, consinfo)
753
    result = q.Query(iqd)
754
    self.assertEqual(len(result), len(instances))
755
    self.assert_(compat.all(len(row) == len(selected)
756
                            for row in result))
757

    
758
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
759
           "Offline nodes not included in bad nodes"
760

    
761
    tested_status = set()
762

    
763
    for (inst, row) in zip(instances, result):
764
      assert inst.primary_node in nodes
765

    
766
      self.assertEqual(row[fieldidx["name"]],
767
                       (constants.RS_NORMAL, inst.name))
768

    
769
      if inst.primary_node in offline_nodes:
770
        exp_status = constants.INSTST_NODEOFFLINE
771
      elif inst.primary_node in bad_nodes:
772
        exp_status = constants.INSTST_NODEDOWN
773
      elif inst.name in live_data:
774
        if inst.name in wrongnode_inst:
775
          exp_status = constants.INSTST_WRONGNODE
776
        elif inst.admin_up:
777
          exp_status = constants.INSTST_RUNNING
778
        else:
779
          exp_status = constants.INSTST_ERRORUP
780
      elif inst.admin_up:
781
        exp_status = constants.INSTST_ERRORDOWN
782
      else:
783
        exp_status = constants.INSTST_ADMINDOWN
784

    
785
      self.assertEqual(row[fieldidx["status"]],
786
                       (constants.RS_NORMAL, exp_status))
787

    
788
      (_, status) = row[fieldidx["status"]]
789
      tested_status.add(status)
790

    
791
      for (field, livefield) in [("oper_ram", "memory"),
792
                                 ("oper_vcpus", "vcpus")]:
793
        if inst.primary_node in bad_nodes:
794
          exp = (constants.RS_NODATA, None)
795
        elif inst.name in live_data:
796
          value = live_data[inst.name].get(livefield, None)
797
          if value is None:
798
            exp = (constants.RS_UNAVAIL, None)
799
          else:
800
            exp = (constants.RS_NORMAL, value)
801
        else:
802
          exp = (constants.RS_UNAVAIL, None)
803

    
804
        self.assertEqual(row[fieldidx[field]], exp)
805

    
806
      bridges = inst_bridges.get(inst.name, [])
807
      self.assertEqual(row[fieldidx["nic.bridges"]],
808
                       (constants.RS_NORMAL, bridges))
809
      if bridges:
810
        self.assertEqual(row[fieldidx["bridge"]],
811
                         (constants.RS_NORMAL, bridges[0]))
812
      else:
813
        self.assertEqual(row[fieldidx["bridge"]],
814
                         (constants.RS_UNAVAIL, None))
815

    
816
      for i in range(constants.MAX_NICS):
817
        if i < len(bridges) and bridges[i] is not None:
818
          exp = (constants.RS_NORMAL, bridges[i])
819
        else:
820
          exp = (constants.RS_UNAVAIL, None)
821
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
822

    
823
      if inst.primary_node in bad_nodes:
824
        exp = (constants.RS_NODATA, None)
825
      else:
826
        exp = (constants.RS_NORMAL, inst.name in live_data)
827
      self.assertEqual(row[fieldidx["oper_state"]], exp)
828

    
829
      usage = disk_usage[inst.name]
830
      if usage is None:
831
        usage = 0
832
      self.assertEqual(row[fieldidx["disk_usage"]],
833
                       (constants.RS_NORMAL, usage))
834

    
835
      for alias, target in [("sda_size", "disk.size/0"),
836
                            ("sdb_size", "disk.size/1"),
837
                            ("vcpus", "be/vcpus"),
838
                            ("ip", "nic.ip/0"),
839
                            ("mac", "nic.mac/0"),
840
                            ("bridge", "nic.bridge/0"),
841
                            ("nic_mode", "nic.mode/0"),
842
                            ("nic_link", "nic.link/0"),
843
                            ]:
844
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
845

    
846
      for field in ["ctime", "mtime"]:
847
        if getattr(inst, field) is None:
848
          # No ctime/mtime
849
          exp = (constants.RS_UNAVAIL, None)
850
        else:
851
          exp = (constants.RS_NORMAL, getattr(inst, field))
852
        self.assertEqual(row[fieldidx[field]], exp)
853

    
854
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
855

    
856
    # Ensure all possible status' have been tested
857
    self.assertEqual(tested_status, constants.INSTST_ALL)
858

    
859
  def _CheckInstanceConsole(self, instance, (status, consdata)):
860
    if instance.name == "inst7":
861
      self.assertEqual(status, constants.RS_NORMAL)
862
      console = objects.InstanceConsole.FromDict(consdata)
863
      self.assertTrue(console.Validate())
864
      self.assertEqual(console.host, instance.primary_node)
865
    else:
866
      self.assertEqual(status, constants.RS_UNAVAIL)
867

    
868

    
869
class TestGroupQuery(unittest.TestCase):
870

    
871
  def setUp(self):
872
    self.groups = [
873
      objects.NodeGroup(name="default",
874
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
875
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
876
      objects.NodeGroup(name="restricted",
877
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
878
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
879
      ]
880

    
881
  def _Create(self, selected):
882
    return query.Query(query.GROUP_FIELDS, selected)
883

    
884
  def testSimple(self):
885
    q = self._Create(["name", "uuid", "alloc_policy"])
886
    gqd = query.GroupQueryData(self.groups, None, None)
887

    
888
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
889

    
890
    self.assertEqual(q.Query(gqd),
891
      [[(constants.RS_NORMAL, "default"),
892
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
893
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
894
        ],
895
       [(constants.RS_NORMAL, "restricted"),
896
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
897
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
898
        ],
899
       ])
900

    
901
  def testNodes(self):
902
    groups_to_nodes = {
903
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
904
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
905
      }
906

    
907
    q = self._Create(["name", "node_cnt", "node_list"])
908
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
909

    
910
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
911

    
912
    self.assertEqual(q.Query(gqd),
913
                     [[(constants.RS_NORMAL, "default"),
914
                       (constants.RS_NORMAL, 2),
915
                       (constants.RS_NORMAL, ["node1", "node2"]),
916
                       ],
917
                      [(constants.RS_NORMAL, "restricted"),
918
                       (constants.RS_NORMAL, 3),
919
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
920
                       ],
921
                      ])
922

    
923
  def testInstances(self):
924
    groups_to_instances = {
925
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
926
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
927
      }
928

    
929
    q = self._Create(["pinst_cnt", "pinst_list"])
930
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
931

    
932
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
933

    
934
    self.assertEqual(q.Query(gqd),
935
                     [[(constants.RS_NORMAL, 2),
936
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
937
                       ],
938
                      [(constants.RS_NORMAL, 3),
939
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
940
                       ],
941
                      ])
942

    
943

    
944
class TestQueryFields(unittest.TestCase):
945
  def testAllFields(self):
946
    for fielddefs in query.ALL_FIELD_LISTS:
947
      result = query.QueryFields(fielddefs, None)
948
      self.assert_(isinstance(result, dict))
949
      response = objects.QueryFieldsResponse.FromDict(result)
950
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
951
        [(fdef2.name, fdef2.title)
952
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
953
                                             key=lambda x: x[0].name)])
954

    
955
  def testSomeFields(self):
956
    rnd = random.Random(5357)
957

    
958
    for _ in range(10):
959
      for fielddefs in query.ALL_FIELD_LISTS:
960
        if len(fielddefs) > 20:
961
          sample_size = rnd.randint(5, 20)
962
        else:
963
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
964
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
965
                                                      sample_size)]
966
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
967
        self.assert_(isinstance(result, dict))
968
        response = objects.QueryFieldsResponse.FromDict(result)
969
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
970
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
971

    
972

    
973
if __name__ == "__main__":
974
  testutils.GanetiTestProgram()