Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.opcodes_unittest.py @ 14933c17

History | View | Annotate | Download (14 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.OpGroupQuery().TinySummary(), "G_QUERY")
132
    self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
133

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

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

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

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

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

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

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

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

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

    
171
      self.assertEqual(all_slots, param_names)
172

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
276
    op = OpTest()
277
    op.Validate(True)
278
    self.assertEqual(op.value1, "default")
279
    self.assertEqual(op.value2, "result")
280
    self.assert_(op.dry_run is None)
281
    self.assert_(op.debug_level is None)
282
    self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
283

    
284
    op = OpTest(value1="hello", value2="world", debug_level=123)
285
    op.Validate(True)
286
    self.assertEqual(op.value1, "hello")
287
    self.assertEqual(op.value2, "world")
288
    self.assertEqual(op.debug_level, 123)
289

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

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

    
303

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

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

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

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

    
335

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

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

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

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

    
367

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

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

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

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

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

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

    
400

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