Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.opcodes_unittest.py @ 081dc516

History | View | Annotate | Download (13.9 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2010, 2011, 2012 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 opcodes_base
31
from ganeti import ht
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import compat
35

    
36
import testutils
37

    
38

    
39
class TestOpcodes(unittest.TestCase):
40
  def test(self):
41
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
42
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
43
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
44
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
45

    
46
    for cls in opcodes.OP_MAPPING.values():
47
      self.assert_(cls.OP_ID.startswith("OP_"))
48
      self.assert_(len(cls.OP_ID) > 3)
49
      self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
50
      self.assertEqual(cls.OP_ID, opcodes_base._NameToId(cls.__name__))
51
      self.assertFalse(
52
        compat.any(cls.OP_ID.startswith(prefix)
53
                   for prefix in opcodes_base.SUMMARY_PREFIX.keys()))
54
      self.assertTrue(callable(cls.OP_RESULT),
55
                      msg=("%s should have a result check" % cls.OP_ID))
56

    
57
      self.assertRaises(TypeError, cls, unsupported_parameter="some value")
58

    
59
      args = [
60
        # No variables
61
        {},
62

    
63
        # Variables supported by all opcodes
64
        {"dry_run": False, "debug_level": 0, },
65

    
66
        # All variables
67
        dict([(name, []) for name in cls.GetAllSlots()])
68
        ]
69

    
70
      for i in args:
71
        op = cls(**i)
72

    
73
        self.assertEqual(op.OP_ID, cls.OP_ID)
74
        self._checkSummary(op)
75

    
76
        # Try a restore
77
        state = op.__getstate__()
78
        self.assert_(isinstance(state, dict))
79

    
80
        restored = opcodes.OpCode.LoadOpCode(state)
81
        self.assert_(isinstance(restored, cls))
82
        self._checkSummary(restored)
83

    
84
        for name in ["x_y_z", "hello_world"]:
85
          assert name not in cls.GetAllSlots()
86
          for value in [None, True, False, [], "Hello World"]:
87
            self.assertRaises(AttributeError, setattr, op, name, value)
88

    
89
  def _checkSummary(self, op):
90
    summary = op.Summary()
91

    
92
    if hasattr(op, "OP_DSC_FIELD"):
93
      self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
94
      self.assert_(summary.endswith(")"))
95
    else:
96
      self.assertEqual("OP_%s" % summary, op.OP_ID)
97

    
98
  def testSummary(self):
99
    class OpTest(opcodes.OpCode):
100
      OP_DSC_FIELD = "data"
101
      OP_PARAMS = [
102
        ("data", ht.NoDefault, ht.TString, None),
103
        ]
104

    
105
    self.assertEqual(OpTest(data="").Summary(), "TEST()")
106
    self.assertEqual(OpTest(data="Hello World").Summary(),
107
                     "TEST(Hello World)")
108
    self.assertEqual(OpTest(data="node1.example.com").Summary(),
109
                     "TEST(node1.example.com)")
110

    
111
  def testSummaryFormatter(self):
112
    class OpTest(opcodes.OpCode):
113
      OP_DSC_FIELD = "data"
114
      OP_DSC_FORMATTER = lambda _, v: "a"
115
      OP_PARAMS = [
116
        ("data", ht.NoDefault, ht.TString, None),
117
        ]
118
    self.assertEqual(OpTest(data="").Summary(), "TEST(a)")
119
    self.assertEqual(OpTest(data="b").Summary(), "TEST(a)")
120

    
121
  def testTinySummary(self):
122
    self.assertFalse(
123
      utils.FindDuplicates(opcodes_base.SUMMARY_PREFIX.values()))
124
    self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
125
                               for (prefix, supplement) in
126
                                 opcodes_base.SUMMARY_PREFIX.items()))
127

    
128
    self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
129
    self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
130
    self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
131
    self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
132

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

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

    
146
  def testOpId(self):
147
    self.assertFalse(utils.FindDuplicates(cls.OP_ID
148
                                          for cls in opcodes._GetOpList()))
149
    self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
150

    
151
  def testParams(self):
152
    supported_by_all = set(["debug_level", "dry_run", "priority"])
153

    
154
    self.assertTrue(opcodes_base.BaseOpCode not in opcodes.OP_MAPPING.values())
155
    self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
156

    
157
    for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
158
      all_slots = cls.GetAllSlots()
159

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

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

    
168
      param_names = [name for (name, _, _, _) in cls.GetAllParams()]
169

    
170
      self.assertEqual(all_slots, param_names)
171

    
172
      # Without inheritance
173
      self.assertEqual(cls.__slots__,
174
                       [name for (name, _, _, _) in cls.OP_PARAMS])
175

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

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

    
190
        if callable(aval):
191
          default_value = aval()
192
          self.assertFalse(callable(default_value),
193
                           msg=("Default value of %s.%s returned by function"
194
                                " is callable" % (cls.OP_ID, attr_name)))
195
        else:
196
          default_value = aval
197

    
198
        if aval is not ht.NoDefault and aval is not None:
199
          self.assertTrue(test(default_value),
200
                          msg=("Default value of %s.%s does not verify" %
201
                               (cls.OP_ID, attr_name)))
202

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

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

    
217
    # Missing required parameter "nodef"
218
    op = OpTest()
219
    before = op.__getstate__()
220
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
221
    self.assertTrue(op.nodef is None)
222
    self.assertEqual(op.wdef, "default")
223
    self.assertEqual(op.number, 0)
224
    self.assertTrue(op.notype is None)
225
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
226

    
227
    # Required parameter "nodef" is provided
228
    op = OpTest(nodef="foo")
229
    before = op.__getstate__()
230
    op.Validate(False)
231
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
232
    self.assertEqual(op.nodef, "foo")
233
    self.assertEqual(op.wdef, "default")
234
    self.assertEqual(op.number, 0)
235
    self.assertTrue(op.notype is None)
236

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

    
245
    # Wrong type for "nodef"
246
    op = OpTest(nodef=987)
247
    before = op.__getstate__()
248
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
249
    self.assertEqual(op.nodef, 987)
250
    self.assertTrue(op.notype is None)
251
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
252

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

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

    
268
  def testValidateSetDefaults(self):
269
    class OpTest(opcodes.OpCode):
270
      OP_PARAMS = [
271
        ("value1", "default", ht.TMaybeString, None),
272
        ("value2", "result", ht.TMaybeString, None),
273
        ]
274

    
275
    op = OpTest()
276
    op.Validate(True)
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
    op.Validate(True)
285
    self.assertEqual(op.value1, "hello")
286
    self.assertEqual(op.value2, "world")
287
    self.assertEqual(op.debug_level, 123)
288

    
289
  def testOpInstanceMultiAlloc(self):
290
    inst = dict([(name, []) for name in opcodes.OpInstanceCreate.GetAllSlots()])
291
    inst_op = opcodes.OpInstanceCreate(**inst)
292
    inst_state = inst_op.__getstate__()
293

    
294
    multialloc = opcodes.OpInstanceMultiAlloc(instances=[inst_op])
295
    state = multialloc.__getstate__()
296
    self.assertEquals(state["instances"], [inst_state])
297
    loaded_multialloc = opcodes.OpCode.LoadOpCode(state)
298
    (loaded_inst,) = loaded_multialloc.instances
299
    self.assertNotEquals(loaded_inst, inst_op)
300
    self.assertEquals(loaded_inst.__getstate__(), inst_state)
301

    
302

    
303
class TestOpcodeDepends(unittest.TestCase):
304
  def test(self):
305
    check_relative = opcodes_base.BuildJobDepCheck(True)
306
    check_norelative = opcodes_base.TNoRelativeJobDependencies
307

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

    
321
      self.assertFalse(fn(1))
322
      self.assertFalse(fn([(9, )]))
323
      self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
324

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

    
334

    
335
class TestResultChecks(unittest.TestCase):
336
  def testJobIdList(self):
337
    for i in [[], [(False, "error")], [(False, "")],
338
              [(True, 123), (True, "999")]]:
339
      self.assertTrue(ht.TJobIdList(i))
340

    
341
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
342
      self.assertFalse(ht.TJobIdList(i))
343

    
344
  def testJobIdListOnly(self):
345
    self.assertTrue(ht.TJobIdListOnly({
346
      constants.JOB_IDS_KEY: [],
347
      }))
348
    self.assertTrue(ht.TJobIdListOnly({
349
      constants.JOB_IDS_KEY: [(True, "9282")],
350
      }))
351

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

    
366

    
367
class TestOpInstanceSetParams(unittest.TestCase):
368
  def _GenericTests(self, fn):
369
    self.assertTrue(fn([]))
370
    self.assertTrue(fn([(constants.DDM_ADD, {})]))
371
    self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
372
    for i in [0, 1, 2, 3, 9, 10, 1024]:
373
      self.assertTrue(fn([(i, {})]))
374

    
375
    self.assertFalse(fn(None))
376
    self.assertFalse(fn({}))
377
    self.assertFalse(fn(""))
378
    self.assertFalse(fn(0))
379
    self.assertFalse(fn([(-100, {})]))
380
    self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
381
    self.assertFalse(fn([[constants.DDM_ADD]]))
382

    
383
  def testNicModifications(self):
384
    fn = ht.TSetParamsMods(ht.TINicParams)
385
    self._GenericTests(fn)
386

    
387
    for param in constants.INIC_PARAMS:
388
      self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
389
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
390

    
391
  def testDiskModifications(self):
392
    fn = ht.TSetParamsMods(ht.TIDiskParams)
393
    self._GenericTests(fn)
394

    
395
    for param in constants.IDISK_PARAMS:
396
      self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
397
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
398

    
399

    
400
if __name__ == "__main__":
401
  testutils.GanetiTestProgram()