Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.query_unittest.py @ 8235fe04

History | View | Annotate | Download (17.7 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

    
27
from ganeti import constants
28
from ganeti import utils
29
from ganeti import compat
30
from ganeti import errors
31
from ganeti import query
32
from ganeti import objects
33

    
34
import testutils
35

    
36

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

    
42

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

    
47
    for name, value in kwargs.items():
48
      setattr(self, name, value)
49

    
50
  def __iter__(self):
51
    return iter(self.data)
52

    
53

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

    
61

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
256

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

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

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

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

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

    
278

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

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

    
292
      q = self._Create(["name", "drained"])
293
      self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
294
      self.assertEqual(q.Query(nqd),
295
                       [[(constants.QRFS_NORMAL, "node1"),
296
                         (constants.QRFS_NORMAL, False)],
297
                        [(constants.QRFS_NORMAL, "node2"),
298
                         (constants.QRFS_NORMAL, True)],
299
                        [(constants.QRFS_NORMAL, "node3"),
300
                         (constants.QRFS_NORMAL, False)],
301
                       ])
302
      self.assertEqual(q.OldStyleQuery(nqd),
303
                       [["node1", False],
304
                        ["node2", True],
305
                        ["node3", False]])
306

    
307
  def test(self):
308
    selected = query.NODE_FIELDS.keys()
309
    field_index = dict((field, idx) for idx, field in enumerate(selected))
310

    
311
    q = self._Create(selected)
312
    self.assertEqual(q.RequestedData(),
313
                     set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
314
                          query.NQ_GROUP]))
315

    
316
    node_names = ["node%s" % i for i in range(20)]
317
    master_name = node_names[3]
318
    nodes = [
319
      objects.Node(name=name,
320
                   primary_ip="192.0.2.%s" % idx,
321
                   secondary_ip="192.0.100.%s" % idx,
322
                   serial_no=7789 * idx,
323
                   master_candidate=(name != master_name and idx % 3 == 0),
324
                   offline=False,
325
                   drained=False,
326
                   vm_capable=False,
327
                   master_capable=False,
328
                   group="default",
329
                   ctime=1290006900,
330
                   mtime=1290006913,
331
                   uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
332
      for idx, name in enumerate(node_names)
333
      ]
334

    
335
    master_node = nodes[3]
336
    master_node.AddTag("masternode")
337
    master_node.AddTag("another")
338
    master_node.AddTag("tag")
339
    assert master_node.name == master_name
340

    
341
    live_data_name = node_names[4]
342
    assert live_data_name != master_name
343

    
344
    fake_live_data = {
345
      "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
346
      "cnodes": 4,
347
      "csockets": 4,
348
      "ctotal": 8,
349
      "mnode": 128,
350
      "mfree": 100,
351
      "mtotal": 4096,
352
      "dfree": 5 * 1024 * 1024,
353
      "dtotal": 100 * 1024 * 1024,
354
      }
355

    
356
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
357
            sorted(fake_live_data.keys()))
358

    
359
    live_data = dict.fromkeys(node_names, {})
360
    live_data[live_data_name] = \
361
      dict((query._NODE_LIVE_FIELDS[name][2], value)
362
           for name, value in fake_live_data.items())
363

    
364
    node_to_primary = dict((name, set()) for name in node_names)
365
    node_to_primary[master_name].update(["inst1", "inst2"])
366

    
367
    node_to_secondary = dict((name, set()) for name in node_names)
368
    node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
369

    
370
    ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
371
    groups = {
372
      ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid),
373
      }
374

    
375
    master_node.group = ng_uuid
376

    
377
    nqd = query.NodeQueryData(nodes, live_data, master_name,
378
                              node_to_primary, node_to_secondary, groups)
379
    result = q.Query(nqd)
380
    self.assert_(compat.all(len(row) == len(selected) for row in result))
381
    self.assertEqual([row[field_index["name"]] for row in result],
382
                     [(constants.QRFS_NORMAL, name) for name in node_names])
383

    
384
    node_to_row = dict((row[field_index["name"]][1], idx)
385
                       for idx, row in enumerate(result))
386

    
387
    master_row = result[node_to_row[master_name]]
388
    self.assert_(master_row[field_index["master"]])
389
    self.assert_(master_row[field_index["role"]], "M")
390
    self.assertEqual(master_row[field_index["group"]],
391
                     (constants.QRFS_NORMAL, "ng1"))
392
    self.assertEqual(master_row[field_index["group.uuid"]],
393
                     (constants.QRFS_NORMAL, ng_uuid))
394

    
395
    self.assert_(row[field_index["pip"]] == node.primary_ip and
396
                 row[field_index["sip"]] == node.secondary_ip and
397
                 set(row[field_index["tags"]]) == node.GetTags() and
398
                 row[field_index["serial_no"]] == node.serial_no and
399
                 row[field_index["role"]] == query._GetNodeRole(node,
400
                                                                master_name) and
401
                 (node.name == master_name or
402
                  (row[field_index["group"]] == "<unknown>" and
403
                   row[field_index["group.uuid"]] is None))
404
                 for row, node in zip(result, nodes))
405

    
406
    live_data_row = result[node_to_row[live_data_name]]
407

    
408
    for (field, value) in fake_live_data.items():
409
      self.assertEqual(live_data_row[field_index[field]],
410
                       (constants.QRFS_NORMAL, value))
411

    
412
    self.assertEqual(master_row[field_index["pinst_cnt"]],
413
                     (constants.QRFS_NORMAL, 2))
414
    self.assertEqual(live_data_row[field_index["sinst_cnt"]],
415
                     (constants.QRFS_NORMAL, 3))
416
    self.assertEqual(master_row[field_index["pinst_list"]],
417
                     (constants.QRFS_NORMAL,
418
                      list(node_to_primary[master_name])))
419
    self.assertEqual(live_data_row[field_index["sinst_list"]],
420
                     (constants.QRFS_NORMAL,
421
                      list(node_to_secondary[live_data_name])))
422

    
423
  def testGetLiveNodeField(self):
424
    nodes = [
425
      objects.Node(name="node1", drained=False),
426
      objects.Node(name="node2", drained=True),
427
      objects.Node(name="node3", drained=False),
428
      ]
429
    live_data = dict.fromkeys([node.name for node in nodes], {})
430

    
431
    # No data
432
    nqd = query.NodeQueryData(None, None, None, None, None, None)
433
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
434
                                             nqd, None),
435
                     (constants.QRFS_NODATA, None))
436

    
437
    # Missing field
438
    ctx = _QueryData(None, curlive_data={
439
      "some": 1,
440
      "other": 2,
441
      })
442
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
443
                                             ctx, None),
444
                     (constants.QRFS_UNAVAIL, None))
445

    
446
    # Wrong format/datatype
447
    ctx = _QueryData(None, curlive_data={
448
      "hello": ["Hello World"],
449
      "other": 2,
450
      })
451
    self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
452
                                             ctx, None),
453
                     (constants.QRFS_UNAVAIL, None))
454

    
455
    # Wrong field type
456
    ctx = _QueryData(None, curlive_data={"hello": 123})
457
    self.assertRaises(AssertionError, query._GetLiveNodeField,
458
                      "hello", constants.QFT_BOOL, ctx, None)
459

    
460

    
461
if __name__ == "__main__":
462
  testutils.GanetiTestProgram()