Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.opcodes_unittest.py @ 415feb2e

History | View | Annotate | Download (14.2 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.OpTagsDel,
42
  opcodes.OpTagsGet,
43
  opcodes.OpTagsSearch,
44
  opcodes.OpTagsSet,
45
  opcodes.OpTestAllocator,
46
  opcodes.OpTestDelay,
47
  opcodes.OpTestDummy,
48
  opcodes.OpTestJqueue,
49
  ])
50

    
51

    
52
class TestOpcodes(unittest.TestCase):
53
  def test(self):
54
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
55
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
56
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
57
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
58

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

    
74
      self.assertRaises(TypeError, cls, unsupported_parameter="some value")
75

    
76
      args = [
77
        # No variables
78
        {},
79

    
80
        # Variables supported by all opcodes
81
        {"dry_run": False, "debug_level": 0, },
82

    
83
        # All variables
84
        dict([(name, False) for name in cls._all_slots()])
85
        ]
86

    
87
      for i in args:
88
        op = cls(**i)
89

    
90
        self.assertEqual(op.OP_ID, cls.OP_ID)
91
        self._checkSummary(op)
92

    
93
        # Try a restore
94
        state = op.__getstate__()
95
        self.assert_(isinstance(state, dict))
96

    
97
        restored = opcodes.OpCode.LoadOpCode(state)
98
        self.assert_(isinstance(restored, cls))
99
        self._checkSummary(restored)
100

    
101
        for name in ["x_y_z", "hello_world"]:
102
          assert name not in cls._all_slots()
103
          for value in [None, True, False, [], "Hello World"]:
104
            self.assertRaises(AttributeError, setattr, op, name, value)
105

    
106
  def _checkSummary(self, op):
107
    summary = op.Summary()
108

    
109
    if hasattr(op, "OP_DSC_FIELD"):
110
      self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
111
      self.assert_(summary.endswith(")"))
112
    else:
113
      self.assertEqual("OP_%s" % summary, op.OP_ID)
114

    
115
  def testSummary(self):
116
    class OpTest(opcodes.OpCode):
117
      OP_DSC_FIELD = "data"
118
      OP_PARAMS = [
119
        ("data", ht.NoDefault, ht.TString, None),
120
        ]
121

    
122
    self.assertEqual(OpTest(data="").Summary(), "TEST()")
123
    self.assertEqual(OpTest(data="Hello World").Summary(),
124
                     "TEST(Hello World)")
125
    self.assertEqual(OpTest(data="node1.example.com").Summary(),
126
                     "TEST(node1.example.com)")
127

    
128
  def testTinySummary(self):
129
    self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
130
    self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
131
                               for (prefix, supplement) in
132
                                 opcodes._SUMMARY_PREFIX.items()))
133

    
134
    self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
135
    self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
136
    self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
137
    self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
138
    self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
139

    
140
  def testListSummary(self):
141
    class OpTest(opcodes.OpCode):
142
      OP_DSC_FIELD = "data"
143
      OP_PARAMS = [
144
        ("data", ht.NoDefault, ht.TList, None),
145
        ]
146

    
147
    self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
148
                     "TEST(a,b,c)")
149
    self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
150
                     "TEST(a,None,c)")
151
    self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
152

    
153
  def testOpId(self):
154
    self.assertFalse(utils.FindDuplicates(cls.OP_ID
155
                                          for cls in opcodes._GetOpList()))
156
    self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
157

    
158
  def testParams(self):
159
    supported_by_all = set(["debug_level", "dry_run", "priority"])
160

    
161
    self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
162
    self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
163

    
164
    for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
165
      all_slots = cls._all_slots()
166

    
167
      self.assertEqual(len(set(all_slots) & supported_by_all), 3,
168
                       msg=("Opcode %s doesn't support all base"
169
                            " parameters (%r)" % (cls.OP_ID, supported_by_all)))
170

    
171
      # All opcodes must have OP_PARAMS
172
      self.assert_(hasattr(cls, "OP_PARAMS"),
173
                   msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
174

    
175
      param_names = [name for (name, _, _, _) in cls.GetAllParams()]
176

    
177
      self.assertEqual(all_slots, param_names)
178

    
179
      # Without inheritance
180
      self.assertEqual(cls.__slots__,
181
                       [name for (name, _, _, _) in cls.OP_PARAMS])
182

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

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

    
197
        if callable(aval):
198
          self.assertFalse(callable(aval()),
199
                           msg="Default value returned by function is callable")
200

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

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

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

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

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

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

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

    
259
    op = OpTest(nodef="foo", notype="Hello World")
260
    before = op.__getstate__()
261
    op.Validate(False)
262
    self.assertEqual(op.nodef, "foo")
263
    self.assertEqual(op.notype, "Hello World")
264
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
265

    
266
  def testValidateSetDefaults(self):
267
    class OpTest(opcodes.OpCode):
268
      OP_PARAMS = [
269
        # Static default value
270
        ("value1", "default", ht.TMaybeString, None),
271

    
272
        # Default value callback
273
        ("value2", lambda: "result", ht.TMaybeString, None),
274
        ]
275

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

    
287
    op = OpTest(value1="hello", value2="world", debug_level=123)
288
    before = op.__getstate__()
289
    op.Validate(True)
290
    self.assertNotEqual(op.__getstate__(), before,
291
                        msg="Opcode was not modified")
292
    self.assertEqual(op.value1, "hello")
293
    self.assertEqual(op.value2, "world")
294
    self.assertEqual(op.debug_level, 123)
295

    
296

    
297
class TestOpcodeDepends(unittest.TestCase):
298
  def test(self):
299
    check_relative = opcodes._BuildJobDepCheck(True)
300
    check_norelative = opcodes.TNoRelativeJobDependencies
301

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

    
315
      self.assertFalse(fn(1))
316
      self.assertFalse(fn([(9, )]))
317
      self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
318

    
319
    for i in [
320
      [(-1, [])],
321
      [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
322
       (-1, [constants.JOB_STATUS_ERROR]),
323
       (9921, [])],
324
      ]:
325
      self.assertTrue(check_relative(i))
326
      self.assertFalse(check_norelative(i))
327

    
328

    
329
class TestResultChecks(unittest.TestCase):
330
  def testJobIdList(self):
331
    for i in [[], [(False, "error")], [(False, "")],
332
              [(True, 123), (True, "999")]]:
333
      self.assertTrue(opcodes.TJobIdList(i))
334

    
335
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
336
      self.assertFalse(opcodes.TJobIdList(i))
337

    
338
  def testJobIdListOnly(self):
339
    self.assertTrue(opcodes.TJobIdListOnly({
340
      constants.JOB_IDS_KEY: [],
341
      }))
342
    self.assertTrue(opcodes.TJobIdListOnly({
343
      constants.JOB_IDS_KEY: [(True, "9282")],
344
      }))
345

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

    
360

    
361
class TestClusterOsList(unittest.TestCase):
362
  def test(self):
363
    good = [
364
      None,
365
      [],
366
      [(constants.DDM_ADD, "dos"),
367
       (constants.DDM_REMOVE, "linux")],
368
      ]
369

    
370
    for i in good:
371
      self.assertTrue(opcodes._TestClusterOsList(i))
372

    
373
    wrong = ["", 0, "xy", ["Hello World"], object(),
374
      [("foo", "bar")],
375
      [("", "")],
376
      [[constants.DDM_ADD]],
377
      [(constants.DDM_ADD, "")],
378
      [(constants.DDM_REMOVE, "")],
379
      [(constants.DDM_ADD, None)],
380
      [(constants.DDM_REMOVE, None)],
381
      ]
382

    
383
    for i in wrong:
384
      self.assertFalse(opcodes._TestClusterOsList(i))
385

    
386

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

    
395
    self.assertFalse(fn(None))
396
    self.assertFalse(fn({}))
397
    self.assertFalse(fn(""))
398
    self.assertFalse(fn(0))
399
    self.assertFalse(fn([(-100, {})]))
400
    self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
401
    self.assertFalse(fn([[constants.DDM_ADD]]))
402

    
403
  def testNicModifications(self):
404
    fn = opcodes.OpInstanceSetParams.TestNicModifications
405
    self._GenericTests(fn)
406

    
407
    for param in constants.INIC_PARAMS:
408
      self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
409
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
410

    
411
  def testDiskModifications(self):
412
    fn = opcodes.OpInstanceSetParams.TestDiskModifications
413
    self._GenericTests(fn)
414

    
415
    for param in constants.IDISK_PARAMS:
416
      self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
417
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
418

    
419

    
420
if __name__ == "__main__":
421
  testutils.GanetiTestProgram()