Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.opcodes_unittest.py @ 202fb4e0

History | View | Annotate | Download (14.5 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.backend"""
23

    
24
import os
25
import sys
26
import unittest
27

    
28
from ganeti import utils
29
from ganeti import opcodes
30
from ganeti import ht
31
from ganeti import constants
32
from ganeti import errors
33
from ganeti import compat
34

    
35
import testutils
36

    
37

    
38
#: Unless an opcode is included in the following list it must have a result
39
#: check of some sort
40
MISSING_RESULT_CHECK = frozenset([
41
  opcodes.OpClusterQuery,
42
  opcodes.OpGroupQuery,
43
  opcodes.OpInstanceQuery,
44
  opcodes.OpInstanceQueryData,
45
  opcodes.OpNodeQuery,
46
  opcodes.OpNodeQueryStorage,
47
  opcodes.OpOsDiagnose,
48
  opcodes.OpQuery,
49
  opcodes.OpQueryFields,
50
  opcodes.OpTagsDel,
51
  opcodes.OpTagsGet,
52
  opcodes.OpTagsSearch,
53
  opcodes.OpTagsSet,
54
  opcodes.OpTestAllocator,
55
  opcodes.OpTestDelay,
56
  opcodes.OpTestDummy,
57
  opcodes.OpTestJqueue,
58
  ])
59

    
60

    
61
class TestOpcodes(unittest.TestCase):
62
  def test(self):
63
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
64
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
65
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
66
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
67

    
68
    for cls in opcodes.OP_MAPPING.values():
69
      self.assert_(cls.OP_ID.startswith("OP_"))
70
      self.assert_(len(cls.OP_ID) > 3)
71
      self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
72
      self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
73
      self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
74
                                  for prefix in opcodes._SUMMARY_PREFIX.keys()))
75
      if cls in MISSING_RESULT_CHECK:
76
        self.assertTrue(cls.OP_RESULT is None,
77
                        msg=("%s is listed to not have a result check" %
78
                             cls.OP_ID))
79
      else:
80
        self.assertTrue(callable(cls.OP_RESULT),
81
                        msg=("%s should have a result check" % cls.OP_ID))
82

    
83
      self.assertRaises(TypeError, cls, unsupported_parameter="some value")
84

    
85
      args = [
86
        # No variables
87
        {},
88

    
89
        # Variables supported by all opcodes
90
        {"dry_run": False, "debug_level": 0, },
91

    
92
        # All variables
93
        dict([(name, False) for name in cls._all_slots()])
94
        ]
95

    
96
      for i in args:
97
        op = cls(**i)
98

    
99
        self.assertEqual(op.OP_ID, cls.OP_ID)
100
        self._checkSummary(op)
101

    
102
        # Try a restore
103
        state = op.__getstate__()
104
        self.assert_(isinstance(state, dict))
105

    
106
        restored = opcodes.OpCode.LoadOpCode(state)
107
        self.assert_(isinstance(restored, cls))
108
        self._checkSummary(restored)
109

    
110
        for name in ["x_y_z", "hello_world"]:
111
          assert name not in cls._all_slots()
112
          for value in [None, True, False, [], "Hello World"]:
113
            self.assertRaises(AttributeError, setattr, op, name, value)
114

    
115
  def _checkSummary(self, op):
116
    summary = op.Summary()
117

    
118
    if hasattr(op, "OP_DSC_FIELD"):
119
      self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
120
      self.assert_(summary.endswith(")"))
121
    else:
122
      self.assertEqual("OP_%s" % summary, op.OP_ID)
123

    
124
  def testSummary(self):
125
    class OpTest(opcodes.OpCode):
126
      OP_DSC_FIELD = "data"
127
      OP_PARAMS = [
128
        ("data", ht.NoDefault, ht.TString, None),
129
        ]
130

    
131
    self.assertEqual(OpTest(data="").Summary(), "TEST()")
132
    self.assertEqual(OpTest(data="Hello World").Summary(),
133
                     "TEST(Hello World)")
134
    self.assertEqual(OpTest(data="node1.example.com").Summary(),
135
                     "TEST(node1.example.com)")
136

    
137
  def testTinySummary(self):
138
    self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
139
    self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
140
                               for (prefix, supplement) in
141
                                 opcodes._SUMMARY_PREFIX.items()))
142

    
143
    self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
144
    self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
145
    self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
146
    self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
147
    self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
148

    
149
  def testListSummary(self):
150
    class OpTest(opcodes.OpCode):
151
      OP_DSC_FIELD = "data"
152
      OP_PARAMS = [
153
        ("data", ht.NoDefault, ht.TList, None),
154
        ]
155

    
156
    self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
157
                     "TEST(a,b,c)")
158
    self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
159
                     "TEST(a,None,c)")
160
    self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
161

    
162
  def testOpId(self):
163
    self.assertFalse(utils.FindDuplicates(cls.OP_ID
164
                                          for cls in opcodes._GetOpList()))
165
    self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
166

    
167
  def testParams(self):
168
    supported_by_all = set(["debug_level", "dry_run", "priority"])
169

    
170
    self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
171
    self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
172

    
173
    for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
174
      all_slots = cls._all_slots()
175

    
176
      self.assertEqual(len(set(all_slots) & supported_by_all), 3,
177
                       msg=("Opcode %s doesn't support all base"
178
                            " parameters (%r)" % (cls.OP_ID, supported_by_all)))
179

    
180
      # All opcodes must have OP_PARAMS
181
      self.assert_(hasattr(cls, "OP_PARAMS"),
182
                   msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
183

    
184
      param_names = [name for (name, _, _, _) in cls.GetAllParams()]
185

    
186
      self.assertEqual(all_slots, param_names)
187

    
188
      # Without inheritance
189
      self.assertEqual(cls.__slots__,
190
                       [name for (name, _, _, _) in cls.OP_PARAMS])
191

    
192
      # This won't work if parameters are converted to a dictionary
193
      duplicates = utils.FindDuplicates(param_names)
194
      self.assertFalse(duplicates,
195
                       msg=("Found duplicate parameters %r in %s" %
196
                            (duplicates, cls.OP_ID)))
197

    
198
      # Check parameter definitions
199
      for attr_name, aval, test, doc in cls.GetAllParams():
200
        self.assert_(attr_name)
201
        self.assert_(test is None or test is ht.NoType or callable(test),
202
                     msg=("Invalid type check for %s.%s" %
203
                          (cls.OP_ID, attr_name)))
204
        self.assertTrue(doc is None or isinstance(doc, basestring))
205

    
206
        if callable(aval):
207
          self.assertFalse(callable(aval()),
208
                           msg="Default value returned by function is callable")
209

    
210
      # If any parameter has documentation, all others need to have it as well
211
      has_doc = [doc is not None for (_, _, _, doc) in cls.OP_PARAMS]
212
      self.assertTrue(not compat.any(has_doc) or compat.all(has_doc),
213
                      msg="%s does not document all parameters" % cls)
214

    
215
  def testValidateNoModification(self):
216
    class OpTest(opcodes.OpCode):
217
      OP_PARAMS = [
218
        ("nodef", ht.NoDefault, ht.TMaybeString, None),
219
        ("wdef", "default", ht.TMaybeString, None),
220
        ("number", 0, ht.TInt, None),
221
        ("notype", None, ht.NoType, None),
222
        ]
223

    
224
    # Missing required parameter "nodef"
225
    op = OpTest()
226
    before = op.__getstate__()
227
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
228
    self.assertFalse(hasattr(op, "nodef"))
229
    self.assertFalse(hasattr(op, "wdef"))
230
    self.assertFalse(hasattr(op, "number"))
231
    self.assertFalse(hasattr(op, "notype"))
232
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
233

    
234
    # Required parameter "nodef" is provided
235
    op = OpTest(nodef="foo")
236
    before = op.__getstate__()
237
    op.Validate(False)
238
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
239
    self.assertEqual(op.nodef, "foo")
240
    self.assertFalse(hasattr(op, "wdef"))
241
    self.assertFalse(hasattr(op, "number"))
242
    self.assertFalse(hasattr(op, "notype"))
243

    
244
    # Missing required parameter "nodef"
245
    op = OpTest(wdef="hello", number=999)
246
    before = op.__getstate__()
247
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
248
    self.assertFalse(hasattr(op, "nodef"))
249
    self.assertFalse(hasattr(op, "notype"))
250
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
251

    
252
    # Wrong type for "nodef"
253
    op = OpTest(nodef=987)
254
    before = op.__getstate__()
255
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
256
    self.assertEqual(op.nodef, 987)
257
    self.assertFalse(hasattr(op, "notype"))
258
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
259

    
260
    # Testing different types for "notype"
261
    op = OpTest(nodef="foo", notype=[1, 2, 3])
262
    before = op.__getstate__()
263
    op.Validate(False)
264
    self.assertEqual(op.nodef, "foo")
265
    self.assertEqual(op.notype, [1, 2, 3])
266
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
267

    
268
    op = OpTest(nodef="foo", notype="Hello World")
269
    before = op.__getstate__()
270
    op.Validate(False)
271
    self.assertEqual(op.nodef, "foo")
272
    self.assertEqual(op.notype, "Hello World")
273
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
274

    
275
  def testValidateSetDefaults(self):
276
    class OpTest(opcodes.OpCode):
277
      OP_PARAMS = [
278
        # Static default value
279
        ("value1", "default", ht.TMaybeString, None),
280

    
281
        # Default value callback
282
        ("value2", lambda: "result", ht.TMaybeString, None),
283
        ]
284

    
285
    op = OpTest()
286
    before = op.__getstate__()
287
    op.Validate(True)
288
    self.assertNotEqual(op.__getstate__(), before,
289
                        msg="Opcode was not modified")
290
    self.assertEqual(op.value1, "default")
291
    self.assertEqual(op.value2, "result")
292
    self.assert_(op.dry_run is None)
293
    self.assert_(op.debug_level is None)
294
    self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
295

    
296
    op = OpTest(value1="hello", value2="world", debug_level=123)
297
    before = op.__getstate__()
298
    op.Validate(True)
299
    self.assertNotEqual(op.__getstate__(), before,
300
                        msg="Opcode was not modified")
301
    self.assertEqual(op.value1, "hello")
302
    self.assertEqual(op.value2, "world")
303
    self.assertEqual(op.debug_level, 123)
304

    
305

    
306
class TestOpcodeDepends(unittest.TestCase):
307
  def test(self):
308
    check_relative = opcodes._BuildJobDepCheck(True)
309
    check_norelative = opcodes.TNoRelativeJobDependencies
310

    
311
    for fn in [check_relative, check_norelative]:
312
      self.assertTrue(fn(None))
313
      self.assertTrue(fn([]))
314
      self.assertTrue(fn([(1, [])]))
315
      self.assertTrue(fn([(719833, [])]))
316
      self.assertTrue(fn([("24879", [])]))
317
      self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
318
      self.assertTrue(fn([
319
        (2028, [constants.JOB_STATUS_ERROR]),
320
        (18750, []),
321
        (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
322
        ]))
323

    
324
      self.assertFalse(fn(1))
325
      self.assertFalse(fn([(9, )]))
326
      self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
327

    
328
    for i in [
329
      [(-1, [])],
330
      [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
331
       (-1, [constants.JOB_STATUS_ERROR]),
332
       (9921, [])],
333
      ]:
334
      self.assertTrue(check_relative(i))
335
      self.assertFalse(check_norelative(i))
336

    
337

    
338
class TestResultChecks(unittest.TestCase):
339
  def testJobIdList(self):
340
    for i in [[], [(False, "error")], [(False, "")],
341
              [(True, 123), (True, "999")]]:
342
      self.assertTrue(opcodes.TJobIdList(i))
343

    
344
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
345
      self.assertFalse(opcodes.TJobIdList(i))
346

    
347
  def testJobIdListOnly(self):
348
    self.assertTrue(opcodes.TJobIdListOnly({
349
      constants.JOB_IDS_KEY: [],
350
      }))
351
    self.assertTrue(opcodes.TJobIdListOnly({
352
      constants.JOB_IDS_KEY: [(True, "9282")],
353
      }))
354

    
355
    self.assertFalse(opcodes.TJobIdListOnly({
356
      "x": None,
357
      }))
358
    self.assertFalse(opcodes.TJobIdListOnly({
359
      constants.JOB_IDS_KEY: [],
360
      "x": None,
361
      }))
362
    self.assertFalse(opcodes.TJobIdListOnly({
363
      constants.JOB_IDS_KEY: [("foo", "bar")],
364
      }))
365
    self.assertFalse(opcodes.TJobIdListOnly({
366
      constants.JOB_IDS_KEY: [("one", "two", "three")],
367
      }))
368

    
369

    
370
class TestClusterOsList(unittest.TestCase):
371
  def test(self):
372
    good = [
373
      None,
374
      [],
375
      [(constants.DDM_ADD, "dos"),
376
       (constants.DDM_REMOVE, "linux")],
377
      ]
378

    
379
    for i in good:
380
      self.assertTrue(opcodes._TestClusterOsList(i))
381

    
382
    wrong = ["", 0, "xy", ["Hello World"], object(),
383
      [("foo", "bar")],
384
      [("", "")],
385
      [[constants.DDM_ADD]],
386
      [(constants.DDM_ADD, "")],
387
      [(constants.DDM_REMOVE, "")],
388
      [(constants.DDM_ADD, None)],
389
      [(constants.DDM_REMOVE, None)],
390
      ]
391

    
392
    for i in wrong:
393
      self.assertFalse(opcodes._TestClusterOsList(i))
394

    
395

    
396
class TestOpInstanceSetParams(unittest.TestCase):
397
  def _GenericTests(self, fn):
398
    self.assertTrue(fn([]))
399
    self.assertTrue(fn([(constants.DDM_ADD, {})]))
400
    self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
401
    for i in [0, 1, 2, 3, 9, 10, 1024]:
402
      self.assertTrue(fn([(i, {})]))
403

    
404
    self.assertFalse(fn(None))
405
    self.assertFalse(fn({}))
406
    self.assertFalse(fn(""))
407
    self.assertFalse(fn(0))
408
    self.assertFalse(fn([(-100, {})]))
409
    self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
410
    self.assertFalse(fn([[constants.DDM_ADD]]))
411

    
412
  def testNicModifications(self):
413
    fn = opcodes.OpInstanceSetParams.TestNicModifications
414
    self._GenericTests(fn)
415

    
416
    for param in constants.INIC_PARAMS:
417
      self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
418
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
419

    
420
  def testDiskModifications(self):
421
    fn = opcodes.OpInstanceSetParams.TestDiskModifications
422
    self._GenericTests(fn)
423

    
424
    for param in constants.IDISK_PARAMS:
425
      self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
426
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
427

    
428

    
429
if __name__ == "__main__":
430
  testutils.GanetiTestProgram()