Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.opcodes_unittest.py @ 48796673

History | View | Annotate | Download (14.1 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.OpTestAllocator,
42
  opcodes.OpTestDelay,
43
  opcodes.OpTestDummy,
44
  opcodes.OpTestJqueue,
45
  ])
46

    
47

    
48
class TestOpcodes(unittest.TestCase):
49
  def test(self):
50
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
51
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
52
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
53
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
54

    
55
    for cls in opcodes.OP_MAPPING.values():
56
      self.assert_(cls.OP_ID.startswith("OP_"))
57
      self.assert_(len(cls.OP_ID) > 3)
58
      self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
59
      self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
60
      self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
61
                                  for prefix in opcodes._SUMMARY_PREFIX.keys()))
62
      if cls in MISSING_RESULT_CHECK:
63
        self.assertTrue(cls.OP_RESULT is None,
64
                        msg=("%s is listed to not have a result check" %
65
                             cls.OP_ID))
66
      else:
67
        self.assertTrue(callable(cls.OP_RESULT),
68
                        msg=("%s should have a result check" % cls.OP_ID))
69

    
70
      self.assertRaises(TypeError, cls, unsupported_parameter="some value")
71

    
72
      args = [
73
        # No variables
74
        {},
75

    
76
        # Variables supported by all opcodes
77
        {"dry_run": False, "debug_level": 0, },
78

    
79
        # All variables
80
        dict([(name, False) for name in cls._all_slots()])
81
        ]
82

    
83
      for i in args:
84
        op = cls(**i)
85

    
86
        self.assertEqual(op.OP_ID, cls.OP_ID)
87
        self._checkSummary(op)
88

    
89
        # Try a restore
90
        state = op.__getstate__()
91
        self.assert_(isinstance(state, dict))
92

    
93
        restored = opcodes.OpCode.LoadOpCode(state)
94
        self.assert_(isinstance(restored, cls))
95
        self._checkSummary(restored)
96

    
97
        for name in ["x_y_z", "hello_world"]:
98
          assert name not in cls._all_slots()
99
          for value in [None, True, False, [], "Hello World"]:
100
            self.assertRaises(AttributeError, setattr, op, name, value)
101

    
102
  def _checkSummary(self, op):
103
    summary = op.Summary()
104

    
105
    if hasattr(op, "OP_DSC_FIELD"):
106
      self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
107
      self.assert_(summary.endswith(")"))
108
    else:
109
      self.assertEqual("OP_%s" % summary, op.OP_ID)
110

    
111
  def testSummary(self):
112
    class OpTest(opcodes.OpCode):
113
      OP_DSC_FIELD = "data"
114
      OP_PARAMS = [
115
        ("data", ht.NoDefault, ht.TString, None),
116
        ]
117

    
118
    self.assertEqual(OpTest(data="").Summary(), "TEST()")
119
    self.assertEqual(OpTest(data="Hello World").Summary(),
120
                     "TEST(Hello World)")
121
    self.assertEqual(OpTest(data="node1.example.com").Summary(),
122
                     "TEST(node1.example.com)")
123

    
124
  def testTinySummary(self):
125
    self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
126
    self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
127
                               for (prefix, supplement) in
128
                                 opcodes._SUMMARY_PREFIX.items()))
129

    
130
    self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
131
    self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
132
    self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
133
    self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
134
    self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
135

    
136
  def testListSummary(self):
137
    class OpTest(opcodes.OpCode):
138
      OP_DSC_FIELD = "data"
139
      OP_PARAMS = [
140
        ("data", ht.NoDefault, ht.TList, None),
141
        ]
142

    
143
    self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
144
                     "TEST(a,b,c)")
145
    self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
146
                     "TEST(a,None,c)")
147
    self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
148

    
149
  def testOpId(self):
150
    self.assertFalse(utils.FindDuplicates(cls.OP_ID
151
                                          for cls in opcodes._GetOpList()))
152
    self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
153

    
154
  def testParams(self):
155
    supported_by_all = set(["debug_level", "dry_run", "priority"])
156

    
157
    self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
158
    self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
159

    
160
    for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
161
      all_slots = cls._all_slots()
162

    
163
      self.assertEqual(len(set(all_slots) & supported_by_all), 3,
164
                       msg=("Opcode %s doesn't support all base"
165
                            " parameters (%r)" % (cls.OP_ID, supported_by_all)))
166

    
167
      # All opcodes must have OP_PARAMS
168
      self.assert_(hasattr(cls, "OP_PARAMS"),
169
                   msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
170

    
171
      param_names = [name for (name, _, _, _) in cls.GetAllParams()]
172

    
173
      self.assertEqual(all_slots, param_names)
174

    
175
      # Without inheritance
176
      self.assertEqual(cls.__slots__,
177
                       [name for (name, _, _, _) in cls.OP_PARAMS])
178

    
179
      # This won't work if parameters are converted to a dictionary
180
      duplicates = utils.FindDuplicates(param_names)
181
      self.assertFalse(duplicates,
182
                       msg=("Found duplicate parameters %r in %s" %
183
                            (duplicates, cls.OP_ID)))
184

    
185
      # Check parameter definitions
186
      for attr_name, aval, test, doc in cls.GetAllParams():
187
        self.assert_(attr_name)
188
        self.assert_(test is None or test is ht.NoType or callable(test),
189
                     msg=("Invalid type check for %s.%s" %
190
                          (cls.OP_ID, attr_name)))
191
        self.assertTrue(doc is None or isinstance(doc, basestring))
192

    
193
        if callable(aval):
194
          self.assertFalse(callable(aval()),
195
                           msg="Default value returned by function is callable")
196

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

    
202
  def testValidateNoModification(self):
203
    class OpTest(opcodes.OpCode):
204
      OP_PARAMS = [
205
        ("nodef", ht.NoDefault, ht.TMaybeString, None),
206
        ("wdef", "default", ht.TMaybeString, None),
207
        ("number", 0, ht.TInt, None),
208
        ("notype", None, ht.NoType, None),
209
        ]
210

    
211
    # Missing required parameter "nodef"
212
    op = OpTest()
213
    before = op.__getstate__()
214
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
215
    self.assertFalse(hasattr(op, "nodef"))
216
    self.assertFalse(hasattr(op, "wdef"))
217
    self.assertFalse(hasattr(op, "number"))
218
    self.assertFalse(hasattr(op, "notype"))
219
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
220

    
221
    # Required parameter "nodef" is provided
222
    op = OpTest(nodef="foo")
223
    before = op.__getstate__()
224
    op.Validate(False)
225
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
226
    self.assertEqual(op.nodef, "foo")
227
    self.assertFalse(hasattr(op, "wdef"))
228
    self.assertFalse(hasattr(op, "number"))
229
    self.assertFalse(hasattr(op, "notype"))
230

    
231
    # Missing required parameter "nodef"
232
    op = OpTest(wdef="hello", number=999)
233
    before = op.__getstate__()
234
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
235
    self.assertFalse(hasattr(op, "nodef"))
236
    self.assertFalse(hasattr(op, "notype"))
237
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
238

    
239
    # Wrong type for "nodef"
240
    op = OpTest(nodef=987)
241
    before = op.__getstate__()
242
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
243
    self.assertEqual(op.nodef, 987)
244
    self.assertFalse(hasattr(op, "notype"))
245
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
246

    
247
    # Testing different types for "notype"
248
    op = OpTest(nodef="foo", notype=[1, 2, 3])
249
    before = op.__getstate__()
250
    op.Validate(False)
251
    self.assertEqual(op.nodef, "foo")
252
    self.assertEqual(op.notype, [1, 2, 3])
253
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
254

    
255
    op = OpTest(nodef="foo", notype="Hello World")
256
    before = op.__getstate__()
257
    op.Validate(False)
258
    self.assertEqual(op.nodef, "foo")
259
    self.assertEqual(op.notype, "Hello World")
260
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
261

    
262
  def testValidateSetDefaults(self):
263
    class OpTest(opcodes.OpCode):
264
      OP_PARAMS = [
265
        # Static default value
266
        ("value1", "default", ht.TMaybeString, None),
267

    
268
        # Default value callback
269
        ("value2", lambda: "result", ht.TMaybeString, None),
270
        ]
271

    
272
    op = OpTest()
273
    before = op.__getstate__()
274
    op.Validate(True)
275
    self.assertNotEqual(op.__getstate__(), before,
276
                        msg="Opcode was not modified")
277
    self.assertEqual(op.value1, "default")
278
    self.assertEqual(op.value2, "result")
279
    self.assert_(op.dry_run is None)
280
    self.assert_(op.debug_level is None)
281
    self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
282

    
283
    op = OpTest(value1="hello", value2="world", debug_level=123)
284
    before = op.__getstate__()
285
    op.Validate(True)
286
    self.assertNotEqual(op.__getstate__(), before,
287
                        msg="Opcode was not modified")
288
    self.assertEqual(op.value1, "hello")
289
    self.assertEqual(op.value2, "world")
290
    self.assertEqual(op.debug_level, 123)
291

    
292

    
293
class TestOpcodeDepends(unittest.TestCase):
294
  def test(self):
295
    check_relative = opcodes._BuildJobDepCheck(True)
296
    check_norelative = opcodes.TNoRelativeJobDependencies
297

    
298
    for fn in [check_relative, check_norelative]:
299
      self.assertTrue(fn(None))
300
      self.assertTrue(fn([]))
301
      self.assertTrue(fn([(1, [])]))
302
      self.assertTrue(fn([(719833, [])]))
303
      self.assertTrue(fn([("24879", [])]))
304
      self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
305
      self.assertTrue(fn([
306
        (2028, [constants.JOB_STATUS_ERROR]),
307
        (18750, []),
308
        (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
309
        ]))
310

    
311
      self.assertFalse(fn(1))
312
      self.assertFalse(fn([(9, )]))
313
      self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
314

    
315
    for i in [
316
      [(-1, [])],
317
      [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
318
       (-1, [constants.JOB_STATUS_ERROR]),
319
       (9921, [])],
320
      ]:
321
      self.assertTrue(check_relative(i))
322
      self.assertFalse(check_norelative(i))
323

    
324

    
325
class TestResultChecks(unittest.TestCase):
326
  def testJobIdList(self):
327
    for i in [[], [(False, "error")], [(False, "")],
328
              [(True, 123), (True, "999")]]:
329
      self.assertTrue(opcodes.TJobIdList(i))
330

    
331
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
332
      self.assertFalse(opcodes.TJobIdList(i))
333

    
334
  def testJobIdListOnly(self):
335
    self.assertTrue(opcodes.TJobIdListOnly({
336
      constants.JOB_IDS_KEY: [],
337
      }))
338
    self.assertTrue(opcodes.TJobIdListOnly({
339
      constants.JOB_IDS_KEY: [(True, "9282")],
340
      }))
341

    
342
    self.assertFalse(opcodes.TJobIdListOnly({
343
      "x": None,
344
      }))
345
    self.assertFalse(opcodes.TJobIdListOnly({
346
      constants.JOB_IDS_KEY: [],
347
      "x": None,
348
      }))
349
    self.assertFalse(opcodes.TJobIdListOnly({
350
      constants.JOB_IDS_KEY: [("foo", "bar")],
351
      }))
352
    self.assertFalse(opcodes.TJobIdListOnly({
353
      constants.JOB_IDS_KEY: [("one", "two", "three")],
354
      }))
355

    
356

    
357
class TestClusterOsList(unittest.TestCase):
358
  def test(self):
359
    good = [
360
      None,
361
      [],
362
      [(constants.DDM_ADD, "dos"),
363
       (constants.DDM_REMOVE, "linux")],
364
      ]
365

    
366
    for i in good:
367
      self.assertTrue(opcodes._TestClusterOsList(i))
368

    
369
    wrong = ["", 0, "xy", ["Hello World"], object(),
370
      [("foo", "bar")],
371
      [("", "")],
372
      [[constants.DDM_ADD]],
373
      [(constants.DDM_ADD, "")],
374
      [(constants.DDM_REMOVE, "")],
375
      [(constants.DDM_ADD, None)],
376
      [(constants.DDM_REMOVE, None)],
377
      ]
378

    
379
    for i in wrong:
380
      self.assertFalse(opcodes._TestClusterOsList(i))
381

    
382

    
383
class TestOpInstanceSetParams(unittest.TestCase):
384
  def _GenericTests(self, fn):
385
    self.assertTrue(fn([]))
386
    self.assertTrue(fn([(constants.DDM_ADD, {})]))
387
    self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
388
    for i in [0, 1, 2, 3, 9, 10, 1024]:
389
      self.assertTrue(fn([(i, {})]))
390

    
391
    self.assertFalse(fn(None))
392
    self.assertFalse(fn({}))
393
    self.assertFalse(fn(""))
394
    self.assertFalse(fn(0))
395
    self.assertFalse(fn([(-100, {})]))
396
    self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
397
    self.assertFalse(fn([[constants.DDM_ADD]]))
398

    
399
  def testNicModifications(self):
400
    fn = opcodes.OpInstanceSetParams.TestNicModifications
401
    self._GenericTests(fn)
402

    
403
    for param in constants.INIC_PARAMS:
404
      self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
405
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
406

    
407
  def testDiskModifications(self):
408
    fn = opcodes.OpInstanceSetParams.TestDiskModifications
409
    self._GenericTests(fn)
410

    
411
    for param in constants.IDISK_PARAMS:
412
      self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
413
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
414

    
415

    
416
if __name__ == "__main__":
417
  testutils.GanetiTestProgram()