Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 79b2ca83

History | View | Annotate | Download (36.3 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
                   vm_capable=True,
372
                   master_capable=False,
373
                   ndparams={},
374
                   group="default",
375
                   ctime=1290006900,
376
                   mtime=1290006913,
377
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
378
      for idx, name in enumerate(node_names)
379
      ]
380

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

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

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

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

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

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

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

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

    
423
    oob_support = dict((name, False) for name in node_names)
424

    
425
    master_node.group = ng_uuid
426

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

    
435
    node_to_row = dict((row[field_index["name"]][1], idx)
436
                       for idx, row in enumerate(result))
437

    
438
    master_row = result[node_to_row[master_name]]
439
    self.assert_(master_row[field_index["master"]])
440
    self.assert_(master_row[field_index["role"]], "M")
441
    self.assertEqual(master_row[field_index["group"]],
442
                     (constants.RS_NORMAL, "ng1"))
443
    self.assertEqual(master_row[field_index["group.uuid"]],
444
                     (constants.RS_NORMAL, ng_uuid))
445
    self.assertEqual(master_row[field_index["ctime"]],
446
                     (constants.RS_UNAVAIL, None))
447
    self.assertEqual(master_row[field_index["mtime"]],
448
                     (constants.RS_UNAVAIL, None))
449

    
450
    self.assert_(row[field_index["pip"]] == node.primary_ip and
451
                 row[field_index["sip"]] == node.secondary_ip and
452
                 set(row[field_index["tags"]]) == node.GetTags() and
453
                 row[field_index["serial_no"]] == node.serial_no and
454
                 row[field_index["role"]] == query._GetNodeRole(node,
455
                                                                master_name) and
456
                 (node.name == master_name or
457
                  (row[field_index["group"]] == "<unknown>" and
458
                   row[field_index["group.uuid"]] is None and
459
                   row[field_index["ctime"]] == (constants.RS_NORMAL,
460
                                                 node.ctime) and
461
                   row[field_index["mtime"]] == (constants.RS_NORMAL,
462
                                                 node.mtime)))
463
                 for row, node in zip(result, nodes))
464

    
465
    live_data_row = result[node_to_row[live_data_name]]
466

    
467
    for (field, value) in fake_live_data.items():
468
      self.assertEqual(live_data_row[field_index[field]],
469
                       (constants.RS_NORMAL, value))
470

    
471
    self.assertEqual(master_row[field_index["pinst_cnt"]],
472
                     (constants.RS_NORMAL, 2))
473
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
474
                     (constants.RS_NORMAL, 3))
475
    self.assertEqual(master_row[field_index["pinst_list"]],
476
                     (constants.RS_NORMAL,
477
                      list(node_to_primary[master_name])))
478
    self.assertEqual(live_data_row[field_index["sinst_list"]],
479
                     (constants.RS_NORMAL,
480
                      list(node_to_secondary[live_data_name])))
481

    
482
  def testGetLiveNodeField(self):
483
    nodes = [
484
      objects.Node(name="node1", drained=False, offline=False,
485
                   vm_capable=True),
486
      objects.Node(name="node2", drained=True, offline=False,
487
                   vm_capable=True),
488
      objects.Node(name="node3", drained=False, offline=False,
489
                   vm_capable=True),
490
      objects.Node(name="node4", drained=False, offline=True,
491
                   vm_capable=True),
492
      objects.Node(name="node5", drained=False, offline=False,
493
                   vm_capable=False),
494
      ]
495
    live_data = dict.fromkeys([node.name for node in nodes], {})
496

    
497
    # No data
498
    nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
499
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
500
                                             nqd, nodes[0]),
501
                     query._FS_NODATA)
502

    
503
    # Missing field
504
    ctx = _QueryData(None, curlive_data={
505
      "some": 1,
506
      "other": 2,
507
      })
508
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
509
                                             ctx, nodes[0]),
510
                     query._FS_UNAVAIL)
511

    
512
    # Wrong format/datatype
513
    ctx = _QueryData(None, curlive_data={
514
      "hello": ["Hello World"],
515
      "other": 2,
516
      })
517
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
518
                                             ctx, nodes[0]),
519
                     query._FS_UNAVAIL)
520

    
521
    # Offline node
522
    assert nodes[3].offline
523
    ctx = _QueryData(None, curlive_data={})
524
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
525
                                             ctx, nodes[3]),
526
                     query._FS_OFFLINE, None)
527

    
528
    # Wrong field type
529
    ctx = _QueryData(None, curlive_data={"hello": 123})
530
    self.assertRaises(AssertionError, query._GetLiveNodeField,
531
                      "hello", constants.QFT_BOOL, ctx, nodes[0])
532

    
533
    # Non-vm_capable node
534
    assert not nodes[4].vm_capable
535
    ctx = _QueryData(None, curlive_data={})
536
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
537
                                             ctx, nodes[4]),
538
                     query._FS_UNAVAIL, None)
539

    
540

    
541
class TestInstanceQuery(unittest.TestCase):
542
  def _Create(self, selected):
543
    return query.Query(query.INSTANCE_FIELDS, selected)
544

    
545
  def testSimple(self):
546
    q = self._Create(["name", "be/memory", "ip"])
547
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
548

    
549
    cluster = objects.Cluster(cluster_name="testcluster",
550
      hvparams=constants.HVC_DEFAULTS,
551
      beparams={
552
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
553
        },
554
      nicparams={
555
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
556
        })
557

    
558
    instances = [
559
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
560
      objects.Instance(name="inst2", hvparams={}, nics=[],
561
        beparams={
562
          constants.BE_MEMORY: 512,
563
        }),
564
      objects.Instance(name="inst3", hvparams={}, beparams={},
565
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
566
      ]
567

    
568
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
569
                                  set(), {})
570
    self.assertEqual(q.Query(iqd),
571
      [[(constants.RS_NORMAL, "inst1"),
572
        (constants.RS_NORMAL, 128),
573
        (constants.RS_UNAVAIL, None),
574
       ],
575
       [(constants.RS_NORMAL, "inst2"),
576
        (constants.RS_NORMAL, 512),
577
        (constants.RS_UNAVAIL, None),
578
       ],
579
       [(constants.RS_NORMAL, "inst3"),
580
        (constants.RS_NORMAL, 128),
581
        (constants.RS_NORMAL, "192.0.2.99"),
582
       ]])
583
    self.assertEqual(q.OldStyleQuery(iqd),
584
      [["inst1", 128, None],
585
       ["inst2", 512, None],
586
       ["inst3", 128, "192.0.2.99"]])
587

    
588
  def test(self):
589
    selected = query.INSTANCE_FIELDS.keys()
590
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
591

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

    
595
    q = self._Create(selected)
596
    self.assertEqual(q.RequestedData(),
597
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
598
                          query.IQ_CONSOLE]))
599

    
600
    cluster = objects.Cluster(cluster_name="testcluster",
601
      hvparams=constants.HVC_DEFAULTS,
602
      beparams={
603
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
604
        },
605
      nicparams={
606
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
607
        },
608
      os_hvp={},
609
      tcpudp_port_pool=set())
610

    
611
    offline_nodes = ["nodeoff1", "nodeoff2"]
612
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
613
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
614

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

    
700
    assert not utils.FindDuplicates(inst.name for inst in instances)
701

    
702
    instbyname = dict((inst.name, inst) for inst in instances)
703

    
704
    disk_usage = dict((inst.name,
705
                       cmdlib._ComputeDiskSize(inst.disk_template,
706
                                               [{"size": disk.size}
707
                                                for disk in inst.disks]))
708
                      for inst in instances)
709

    
710
    inst_bridges = {
711
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
712
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
713
                None, "eth123"],
714
      }
715

    
716
    live_data = {
717
      "inst2": {
718
        "vcpus": 3,
719
        },
720
      "inst4": {
721
        "memory": 123,
722
        },
723
      "inst6": {
724
        "memory": 768,
725
        },
726
      "inst7": {
727
        "vcpus": 3,
728
        },
729
      }
730
    wrongnode_inst = set(["inst7"])
731

    
732
    consinfo = dict((inst.name, None) for inst in instances)
733
    consinfo["inst7"] = \
734
      objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
735
                              host=instbyname["inst7"].primary_node,
736
                              user=constants.GANETI_RUNAS,
737
                              command=["hostname"]).ToDict()
738

    
739
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
740
                                  offline_nodes, bad_nodes, live_data,
741
                                  wrongnode_inst, consinfo)
742
    result = q.Query(iqd)
743
    self.assertEqual(len(result), len(instances))
744
    self.assert_(compat.all(len(row) == len(selected)
745
                            for row in result))
746

    
747
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
748
           "Offline nodes not included in bad nodes"
749

    
750
    tested_status = set()
751

    
752
    for (inst, row) in zip(instances, result):
753
      assert inst.primary_node in nodes
754

    
755
      self.assertEqual(row[fieldidx["name"]],
756
                       (constants.RS_NORMAL, inst.name))
757

    
758
      if inst.primary_node in offline_nodes:
759
        exp_status = constants.INSTST_NODEOFFLINE
760
      elif inst.primary_node in bad_nodes:
761
        exp_status = constants.INSTST_NODEDOWN
762
      elif inst.name in live_data:
763
        if inst.name in wrongnode_inst:
764
          exp_status = constants.INSTST_WRONGNODE
765
        elif inst.admin_up:
766
          exp_status = constants.INSTST_RUNNING
767
        else:
768
          exp_status = constants.INSTST_ERRORUP
769
      elif inst.admin_up:
770
        exp_status = constants.INSTST_ERRORDOWN
771
      else:
772
        exp_status = constants.INSTST_ADMINDOWN
773

    
774
      self.assertEqual(row[fieldidx["status"]],
775
                       (constants.RS_NORMAL, exp_status))
776

    
777
      (_, status) = row[fieldidx["status"]]
778
      tested_status.add(status)
779

    
780
      for (field, livefield) in [("oper_ram", "memory"),
781
                                 ("oper_vcpus", "vcpus")]:
782
        if inst.primary_node in bad_nodes:
783
          exp = (constants.RS_NODATA, None)
784
        elif inst.name in live_data:
785
          value = live_data[inst.name].get(livefield, None)
786
          if value is None:
787
            exp = (constants.RS_UNAVAIL, None)
788
          else:
789
            exp = (constants.RS_NORMAL, value)
790
        else:
791
          exp = (constants.RS_UNAVAIL, None)
792

    
793
        self.assertEqual(row[fieldidx[field]], exp)
794

    
795
      bridges = inst_bridges.get(inst.name, [])
796
      self.assertEqual(row[fieldidx["nic.bridges"]],
797
                       (constants.RS_NORMAL, bridges))
798
      if bridges:
799
        self.assertEqual(row[fieldidx["bridge"]],
800
                         (constants.RS_NORMAL, bridges[0]))
801
      else:
802
        self.assertEqual(row[fieldidx["bridge"]],
803
                         (constants.RS_UNAVAIL, None))
804

    
805
      for i in range(constants.MAX_NICS):
806
        if i < len(bridges) and bridges[i] is not None:
807
          exp = (constants.RS_NORMAL, bridges[i])
808
        else:
809
          exp = (constants.RS_UNAVAIL, None)
810
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
811

    
812
      if inst.primary_node in bad_nodes:
813
        exp = (constants.RS_NODATA, None)
814
      else:
815
        exp = (constants.RS_NORMAL, inst.name in live_data)
816
      self.assertEqual(row[fieldidx["oper_state"]], exp)
817

    
818
      usage = disk_usage[inst.name]
819
      if usage is None:
820
        usage = 0
821
      self.assertEqual(row[fieldidx["disk_usage"]],
822
                       (constants.RS_NORMAL, usage))
823

    
824
      for alias, target in [("sda_size", "disk.size/0"),
825
                            ("sdb_size", "disk.size/1"),
826
                            ("vcpus", "be/vcpus"),
827
                            ("ip", "nic.ip/0"),
828
                            ("mac", "nic.mac/0"),
829
                            ("bridge", "nic.bridge/0"),
830
                            ("nic_mode", "nic.mode/0"),
831
                            ("nic_link", "nic.link/0"),
832
                            ]:
833
        self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
834

    
835
      for field in ["ctime", "mtime"]:
836
        if getattr(inst, field) is None:
837
          # No ctime/mtime
838
          exp = (constants.RS_UNAVAIL, None)
839
        else:
840
          exp = (constants.RS_NORMAL, getattr(inst, field))
841
        self.assertEqual(row[fieldidx[field]], exp)
842

    
843
      self._CheckInstanceConsole(inst, row[fieldidx["console"]])
844

    
845
    # Ensure all possible status' have been tested
846
    self.assertEqual(tested_status, constants.INSTST_ALL)
847

    
848
  def _CheckInstanceConsole(self, instance, (status, consdata)):
849
    if instance.name == "inst7":
850
      self.assertEqual(status, constants.RS_NORMAL)
851
      console = objects.InstanceConsole.FromDict(consdata)
852
      self.assertTrue(console.Validate())
853
      self.assertEqual(console.host, instance.primary_node)
854
    else:
855
      self.assertEqual(status, constants.RS_UNAVAIL)
856

    
857

    
858
class TestGroupQuery(unittest.TestCase):
859

    
860
  def setUp(self):
861
    self.groups = [
862
      objects.NodeGroup(name="default",
863
                        uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
864
                        alloc_policy=constants.ALLOC_POLICY_PREFERRED),
865
      objects.NodeGroup(name="restricted",
866
                        uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
867
                        alloc_policy=constants.ALLOC_POLICY_LAST_RESORT),
868
      ]
869

    
870
  def _Create(self, selected):
871
    return query.Query(query.GROUP_FIELDS, selected)
872

    
873
  def testSimple(self):
874
    q = self._Create(["name", "uuid", "alloc_policy"])
875
    gqd = query.GroupQueryData(self.groups, None, None)
876

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

    
879
    self.assertEqual(q.Query(gqd),
880
      [[(constants.RS_NORMAL, "default"),
881
        (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
882
        (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
883
        ],
884
       [(constants.RS_NORMAL, "restricted"),
885
        (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
886
        (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
887
        ],
888
       ])
889

    
890
  def testNodes(self):
891
    groups_to_nodes = {
892
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
893
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
894
      }
895

    
896
    q = self._Create(["name", "node_cnt", "node_list"])
897
    gqd = query.GroupQueryData(self.groups, groups_to_nodes, None)
898

    
899
    self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
900

    
901
    self.assertEqual(q.Query(gqd),
902
                     [[(constants.RS_NORMAL, "default"),
903
                       (constants.RS_NORMAL, 2),
904
                       (constants.RS_NORMAL, ["node1", "node2"]),
905
                       ],
906
                      [(constants.RS_NORMAL, "restricted"),
907
                       (constants.RS_NORMAL, 3),
908
                       (constants.RS_NORMAL, ["node1", "node9", "node10"]),
909
                       ],
910
                      ])
911

    
912
  def testInstances(self):
913
    groups_to_instances = {
914
      "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
915
      "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
916
      }
917

    
918
    q = self._Create(["pinst_cnt", "pinst_list"])
919
    gqd = query.GroupQueryData(self.groups, None, groups_to_instances)
920

    
921
    self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
922

    
923
    self.assertEqual(q.Query(gqd),
924
                     [[(constants.RS_NORMAL, 2),
925
                       (constants.RS_NORMAL, ["inst1", "inst2"]),
926
                       ],
927
                      [(constants.RS_NORMAL, 3),
928
                       (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
929
                       ],
930
                      ])
931

    
932

    
933
class TestQueryFields(unittest.TestCase):
934
  def testAllFields(self):
935
    for fielddefs in query.ALL_FIELD_LISTS:
936
      result = query.QueryFields(fielddefs, None)
937
      self.assert_(isinstance(result, dict))
938
      response = objects.QueryFieldsResponse.FromDict(result)
939
      self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
940
        [(fdef2.name, fdef2.title)
941
         for (fdef2, _, _) in utils.NiceSort(fielddefs.values(),
942
                                             key=lambda x: x[0].name)])
943

    
944
  def testSomeFields(self):
945
    rnd = random.Random(5357)
946

    
947
    for _ in range(10):
948
      for fielddefs in query.ALL_FIELD_LISTS:
949
        if len(fielddefs) > 20:
950
          sample_size = rnd.randint(5, 20)
951
        else:
952
          sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
953
        fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(),
954
                                                      sample_size)]
955
        result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
956
        self.assert_(isinstance(result, dict))
957
        response = objects.QueryFieldsResponse.FromDict(result)
958
        self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
959
                         [(fdef2.name, fdef2.title) for fdef2 in fields])
960

    
961

    
962
if __name__ == "__main__":
963
  testutils.GanetiTestProgram()