Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.opcodes_unittest.py @ 031d2db1

History | View | Annotate | Download (14.3 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.assertFalse(hasattr(op, "nodef"))
223
    self.assertFalse(hasattr(op, "wdef"))
224
    self.assertFalse(hasattr(op, "number"))
225
    self.assertFalse(hasattr(op, "notype"))
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.assertFalse(hasattr(op, "wdef"))
235
    self.assertFalse(hasattr(op, "number"))
236
    self.assertFalse(hasattr(op, "notype"))
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.assertFalse(hasattr(op, "nodef"))
243
    self.assertFalse(hasattr(op, "notype"))
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.assertFalse(hasattr(op, "notype"))
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
    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
  def testOpInstanceMultiAlloc(self):
297
    inst = dict([(name, []) for name in opcodes.OpInstanceCreate.GetAllSlots()])
298
    inst_op = opcodes.OpInstanceCreate(**inst)
299
    inst_state = inst_op.__getstate__()
300

    
301
    multialloc = opcodes.OpInstanceMultiAlloc(instances=[inst_op])
302
    state = multialloc.__getstate__()
303
    self.assertEquals(state["instances"], [inst_state])
304
    loaded_multialloc = opcodes.OpCode.LoadOpCode(state)
305
    (loaded_inst,) = loaded_multialloc.instances
306
    self.assertNotEquals(loaded_inst, inst_op)
307
    self.assertEquals(loaded_inst.__getstate__(), inst_state)
308

    
309

    
310
class TestOpcodeDepends(unittest.TestCase):
311
  def test(self):
312
    check_relative = opcodes_base.BuildJobDepCheck(True)
313
    check_norelative = opcodes_base.TNoRelativeJobDependencies
314

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

    
328
      self.assertFalse(fn(1))
329
      self.assertFalse(fn([(9, )]))
330
      self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
331

    
332
    for i in [
333
      [(-1, [])],
334
      [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
335
       (-1, [constants.JOB_STATUS_ERROR]),
336
       (9921, [])],
337
      ]:
338
      self.assertTrue(check_relative(i))
339
      self.assertFalse(check_norelative(i))
340

    
341

    
342
class TestResultChecks(unittest.TestCase):
343
  def testJobIdList(self):
344
    for i in [[], [(False, "error")], [(False, "")],
345
              [(True, 123), (True, "999")]]:
346
      self.assertTrue(ht.TJobIdList(i))
347

    
348
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
349
      self.assertFalse(ht.TJobIdList(i))
350

    
351
  def testJobIdListOnly(self):
352
    self.assertTrue(ht.TJobIdListOnly({
353
      constants.JOB_IDS_KEY: [],
354
      }))
355
    self.assertTrue(ht.TJobIdListOnly({
356
      constants.JOB_IDS_KEY: [(True, "9282")],
357
      }))
358

    
359
    self.assertFalse(ht.TJobIdListOnly({
360
      "x": None,
361
      }))
362
    self.assertFalse(ht.TJobIdListOnly({
363
      constants.JOB_IDS_KEY: [],
364
      "x": None,
365
      }))
366
    self.assertFalse(ht.TJobIdListOnly({
367
      constants.JOB_IDS_KEY: [("foo", "bar")],
368
      }))
369
    self.assertFalse(ht.TJobIdListOnly({
370
      constants.JOB_IDS_KEY: [("one", "two", "three")],
371
      }))
372

    
373

    
374
class TestOpInstanceSetParams(unittest.TestCase):
375
  def _GenericTests(self, fn):
376
    self.assertTrue(fn([]))
377
    self.assertTrue(fn([(constants.DDM_ADD, {})]))
378
    self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
379
    for i in [0, 1, 2, 3, 9, 10, 1024]:
380
      self.assertTrue(fn([(i, {})]))
381

    
382
    self.assertFalse(fn(None))
383
    self.assertFalse(fn({}))
384
    self.assertFalse(fn(""))
385
    self.assertFalse(fn(0))
386
    self.assertFalse(fn([(-100, {})]))
387
    self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
388
    self.assertFalse(fn([[constants.DDM_ADD]]))
389

    
390
  def testNicModifications(self):
391
    fn = ht.TSetParamsMods(ht.TINicParams)
392
    self._GenericTests(fn)
393

    
394
    for param in constants.INIC_PARAMS:
395
      self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
396
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
397

    
398
  def testDiskModifications(self):
399
    fn = ht.TSetParamsMods(ht.TIDiskParams)
400
    self._GenericTests(fn)
401

    
402
    for param in constants.IDISK_PARAMS:
403
      self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
404
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
405

    
406

    
407
if __name__ == "__main__":
408
  testutils.GanetiTestProgram()