Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.opcodes_unittest.py @ bdfd7802

History | View | Annotate | Download (12 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
class TestOpcodes(unittest.TestCase):
39
  def test(self):
40
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
41
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
42
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
43
    self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
44

    
45
    for cls in opcodes.OP_MAPPING.values():
46
      self.assert_(cls.OP_ID.startswith("OP_"))
47
      self.assert_(len(cls.OP_ID) > 3)
48
      self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
49
      self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
50
      self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
51
                                  for prefix in opcodes._SUMMARY_PREFIX.keys()))
52
      self.assertTrue(cls.OP_RESULT is None or callable(cls.OP_RESULT))
53

    
54
      self.assertRaises(TypeError, cls, unsupported_parameter="some value")
55

    
56
      args = [
57
        # No variables
58
        {},
59

    
60
        # Variables supported by all opcodes
61
        {"dry_run": False, "debug_level": 0, },
62

    
63
        # All variables
64
        dict([(name, False) for name in cls._all_slots()])
65
        ]
66

    
67
      for i in args:
68
        op = cls(**i)
69

    
70
        self.assertEqual(op.OP_ID, cls.OP_ID)
71
        self._checkSummary(op)
72

    
73
        # Try a restore
74
        state = op.__getstate__()
75
        self.assert_(isinstance(state, dict))
76

    
77
        restored = opcodes.OpCode.LoadOpCode(state)
78
        self.assert_(isinstance(restored, cls))
79
        self._checkSummary(restored)
80

    
81
        for name in ["x_y_z", "hello_world"]:
82
          assert name not in cls._all_slots()
83
          for value in [None, True, False, [], "Hello World"]:
84
            self.assertRaises(AttributeError, setattr, op, name, value)
85

    
86
  def _checkSummary(self, op):
87
    summary = op.Summary()
88

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

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

    
102
    self.assertEqual(OpTest(data="").Summary(), "TEST()")
103
    self.assertEqual(OpTest(data="Hello World").Summary(),
104
                     "TEST(Hello World)")
105
    self.assertEqual(OpTest(data="node1.example.com").Summary(),
106
                     "TEST(node1.example.com)")
107

    
108
  def testTinySummary(self):
109
    self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
110
    self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
111
                               for (prefix, supplement) in
112
                                 opcodes._SUMMARY_PREFIX.items()))
113

    
114
    self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
115
    self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
116
    self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
117
    self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
118
    self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
119

    
120
  def testListSummary(self):
121
    class OpTest(opcodes.OpCode):
122
      OP_DSC_FIELD = "data"
123
      OP_PARAMS = [
124
        ("data", ht.NoDefault, ht.TList, None),
125
        ]
126

    
127
    self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
128
                     "TEST(a,b,c)")
129
    self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
130
                     "TEST(a,None,c)")
131
    self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
132

    
133
  def testOpId(self):
134
    self.assertFalse(utils.FindDuplicates(cls.OP_ID
135
                                          for cls in opcodes._GetOpList()))
136
    self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
137

    
138
  def testParams(self):
139
    supported_by_all = set(["debug_level", "dry_run", "priority"])
140

    
141
    self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
142
    self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
143

    
144
    for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
145
      all_slots = cls._all_slots()
146

    
147
      self.assertEqual(len(set(all_slots) & supported_by_all), 3,
148
                       msg=("Opcode %s doesn't support all base"
149
                            " parameters (%r)" % (cls.OP_ID, supported_by_all)))
150

    
151
      # All opcodes must have OP_PARAMS
152
      self.assert_(hasattr(cls, "OP_PARAMS"),
153
                   msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
154

    
155
      param_names = [name for (name, _, _, _) in cls.GetAllParams()]
156

    
157
      self.assertEqual(all_slots, param_names)
158

    
159
      # Without inheritance
160
      self.assertEqual(cls.__slots__,
161
                       [name for (name, _, _, _) in cls.OP_PARAMS])
162

    
163
      # This won't work if parameters are converted to a dictionary
164
      duplicates = utils.FindDuplicates(param_names)
165
      self.assertFalse(duplicates,
166
                       msg=("Found duplicate parameters %r in %s" %
167
                            (duplicates, cls.OP_ID)))
168

    
169
      # Check parameter definitions
170
      for attr_name, aval, test, doc in cls.GetAllParams():
171
        self.assert_(attr_name)
172
        self.assert_(test is None or test is ht.NoType or callable(test),
173
                     msg=("Invalid type check for %s.%s" %
174
                          (cls.OP_ID, attr_name)))
175
        self.assertTrue(doc is None or isinstance(doc, basestring))
176

    
177
        if callable(aval):
178
          self.assertFalse(callable(aval()),
179
                           msg="Default value returned by function is callable")
180

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

    
186
  def testValidateNoModification(self):
187
    class OpTest(opcodes.OpCode):
188
      OP_PARAMS = [
189
        ("nodef", ht.NoDefault, ht.TMaybeString, None),
190
        ("wdef", "default", ht.TMaybeString, None),
191
        ("number", 0, ht.TInt, None),
192
        ("notype", None, ht.NoType, None),
193
        ]
194

    
195
    # Missing required parameter "nodef"
196
    op = OpTest()
197
    before = op.__getstate__()
198
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
199
    self.assertFalse(hasattr(op, "nodef"))
200
    self.assertFalse(hasattr(op, "wdef"))
201
    self.assertFalse(hasattr(op, "number"))
202
    self.assertFalse(hasattr(op, "notype"))
203
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
204

    
205
    # Required parameter "nodef" is provided
206
    op = OpTest(nodef="foo")
207
    before = op.__getstate__()
208
    op.Validate(False)
209
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
210
    self.assertEqual(op.nodef, "foo")
211
    self.assertFalse(hasattr(op, "wdef"))
212
    self.assertFalse(hasattr(op, "number"))
213
    self.assertFalse(hasattr(op, "notype"))
214

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

    
223
    # Wrong type for "nodef"
224
    op = OpTest(nodef=987)
225
    before = op.__getstate__()
226
    self.assertRaises(errors.OpPrereqError, op.Validate, False)
227
    self.assertEqual(op.nodef, 987)
228
    self.assertFalse(hasattr(op, "notype"))
229
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
230

    
231
    # Testing different types for "notype"
232
    op = OpTest(nodef="foo", notype=[1, 2, 3])
233
    before = op.__getstate__()
234
    op.Validate(False)
235
    self.assertEqual(op.nodef, "foo")
236
    self.assertEqual(op.notype, [1, 2, 3])
237
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
238

    
239
    op = OpTest(nodef="foo", notype="Hello World")
240
    before = op.__getstate__()
241
    op.Validate(False)
242
    self.assertEqual(op.nodef, "foo")
243
    self.assertEqual(op.notype, "Hello World")
244
    self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
245

    
246
  def testValidateSetDefaults(self):
247
    class OpTest(opcodes.OpCode):
248
      OP_PARAMS = [
249
        # Static default value
250
        ("value1", "default", ht.TMaybeString, None),
251

    
252
        # Default value callback
253
        ("value2", lambda: "result", ht.TMaybeString, None),
254
        ]
255

    
256
    op = OpTest()
257
    before = op.__getstate__()
258
    op.Validate(True)
259
    self.assertNotEqual(op.__getstate__(), before,
260
                        msg="Opcode was not modified")
261
    self.assertEqual(op.value1, "default")
262
    self.assertEqual(op.value2, "result")
263
    self.assert_(op.dry_run is None)
264
    self.assert_(op.debug_level is None)
265
    self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
266

    
267
    op = OpTest(value1="hello", value2="world", debug_level=123)
268
    before = op.__getstate__()
269
    op.Validate(True)
270
    self.assertNotEqual(op.__getstate__(), before,
271
                        msg="Opcode was not modified")
272
    self.assertEqual(op.value1, "hello")
273
    self.assertEqual(op.value2, "world")
274
    self.assertEqual(op.debug_level, 123)
275

    
276

    
277
class TestOpcodeDepends(unittest.TestCase):
278
  def test(self):
279
    check_relative = opcodes._BuildJobDepCheck(True)
280
    check_norelative = opcodes.TNoRelativeJobDependencies
281

    
282
    for fn in [check_relative, check_norelative]:
283
      self.assertTrue(fn(None))
284
      self.assertTrue(fn([]))
285
      self.assertTrue(fn([(1, [])]))
286
      self.assertTrue(fn([(719833, [])]))
287
      self.assertTrue(fn([("24879", [])]))
288
      self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
289
      self.assertTrue(fn([
290
        (2028, [constants.JOB_STATUS_ERROR]),
291
        (18750, []),
292
        (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
293
        ]))
294

    
295
      self.assertFalse(fn(1))
296
      self.assertFalse(fn([(9, )]))
297
      self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
298

    
299
    for i in [
300
      [(-1, [])],
301
      [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
302
       (-1, [constants.JOB_STATUS_ERROR]),
303
       (9921, [])],
304
      ]:
305
      self.assertTrue(check_relative(i))
306
      self.assertFalse(check_norelative(i))
307

    
308

    
309
class TestResultChecks(unittest.TestCase):
310
  def testJobIdList(self):
311
    for i in [[], [(False, "error")], [(False, "")],
312
              [(True, 123), (True, "999")]]:
313
      self.assertTrue(opcodes.TJobIdList(i))
314

    
315
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
316
      self.assertFalse(opcodes.TJobIdList(i))
317

    
318
  def testJobIdListOnly(self):
319
    self.assertTrue(opcodes.TJobIdListOnly({
320
      constants.JOB_IDS_KEY: [],
321
      }))
322
    self.assertTrue(opcodes.TJobIdListOnly({
323
      constants.JOB_IDS_KEY: [(True, "9282")],
324
      }))
325

    
326
    self.assertFalse(opcodes.TJobIdListOnly({
327
      "x": None,
328
      }))
329
    self.assertFalse(opcodes.TJobIdListOnly({
330
      constants.JOB_IDS_KEY: [],
331
      "x": None,
332
      }))
333
    self.assertFalse(opcodes.TJobIdListOnly({
334
      constants.JOB_IDS_KEY: [("foo", "bar")],
335
      }))
336
    self.assertFalse(opcodes.TJobIdListOnly({
337
      constants.JOB_IDS_KEY: [("one", "two", "three")],
338
      }))
339

    
340

    
341
if __name__ == "__main__":
342
  testutils.GanetiTestProgram()