Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.hooks_unittest.py @ 3fb4f740

History | View | Annotate | Download (9.6 kB)

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

    
4
# Copyright (C) 2006, 2007 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 unittesting the hooks module"""
23

    
24

    
25
import unittest
26
import os
27
import time
28
import tempfile
29
import os.path
30

    
31
from ganeti import errors
32
from ganeti import opcodes
33
from ganeti import mcpu
34
from ganeti import backend
35
from ganeti import constants
36
from ganeti import cmdlib
37
from ganeti import rpc
38
from ganeti.constants import HKR_SUCCESS, HKR_FAIL, HKR_SKIP
39

    
40
from mocks import FakeConfig, FakeProc, FakeContext
41

    
42
class FakeLU(cmdlib.LogicalUnit):
43
  HPATH = "test"
44
  def BuildHooksEnv(self):
45
    return {}, ["localhost"], ["localhost"]
46

    
47
class TestHooksRunner(unittest.TestCase):
48
  """Testing case for HooksRunner"""
49
  def setUp(self):
50
    self.torm = []
51
    self.tmpdir = tempfile.mkdtemp()
52
    self.torm.append((self.tmpdir, True))
53
    self.logdir = tempfile.mkdtemp()
54
    self.torm.append((self.logdir, True))
55
    self.hpath = "fake"
56
    self.ph_dirs = {}
57
    for i in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
58
      dname = "%s/%s-%s.d" % (self.tmpdir, self.hpath, i)
59
      os.mkdir(dname)
60
      self.torm.append((dname, True))
61
      self.ph_dirs[i] = dname
62
    self.hr = backend.HooksRunner(hooks_base_dir=self.tmpdir)
63

    
64
  def tearDown(self):
65
    self.torm.reverse()
66
    for path, kind in self.torm:
67
      if kind:
68
        os.rmdir(path)
69
      else:
70
        os.unlink(path)
71

    
72
  def _rname(self, fname):
73
    return "/".join(fname.split("/")[-2:])
74

    
75
  def testEmpty(self):
76
    """Test no hooks"""
77
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
78
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
79
                           (True, []))
80

    
81
  def testSkipNonExec(self):
82
    """Test skip non-exec file"""
83
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
84
      fname = "%s/test" % self.ph_dirs[phase]
85
      f = open(fname, "w")
86
      f.close()
87
      self.torm.append((fname, False))
88
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
89
                           (True, [(self._rname(fname), HKR_SKIP, "")]))
90

    
91
  def testSkipInvalidName(self):
92
    """Test skip script with invalid name"""
93
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
94
      fname = "%s/a.off" % self.ph_dirs[phase]
95
      f = open(fname, "w")
96
      f.write("#!/bin/sh\nexit 0\n")
97
      f.close()
98
      os.chmod(fname, 0700)
99
      self.torm.append((fname, False))
100
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
101
                           (True, [(self._rname(fname), HKR_SKIP, "")]))
102

    
103
  def testSkipDir(self):
104
    """Test skip directory"""
105
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
106
      fname = "%s/testdir" % self.ph_dirs[phase]
107
      os.mkdir(fname)
108
      self.torm.append((fname, True))
109
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
110
                           (True, [(self._rname(fname), HKR_SKIP, "")]))
111

    
112
  def testSuccess(self):
113
    """Test success execution"""
114
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
115
      fname = "%s/success" % self.ph_dirs[phase]
116
      f = open(fname, "w")
117
      f.write("#!/bin/sh\nexit 0\n")
118
      f.close()
119
      self.torm.append((fname, False))
120
      os.chmod(fname, 0700)
121
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
122
                           (True, [(self._rname(fname), HKR_SUCCESS, "")]))
123

    
124
  def testSymlink(self):
125
    """Test running a symlink"""
126
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
127
      fname = "%s/success" % self.ph_dirs[phase]
128
      os.symlink("/bin/true", fname)
129
      self.torm.append((fname, False))
130
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
131
                           (True, [(self._rname(fname), HKR_SUCCESS, "")]))
132

    
133
  def testFail(self):
134
    """Test success execution"""
135
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
136
      fname = "%s/success" % self.ph_dirs[phase]
137
      f = open(fname, "w")
138
      f.write("#!/bin/sh\nexit 1\n")
139
      f.close()
140
      self.torm.append((fname, False))
141
      os.chmod(fname, 0700)
142
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
143
                           (True, [(self._rname(fname), HKR_FAIL, "")]))
144

    
145
  def testCombined(self):
146
    """Test success, failure and skip all in one test"""
147
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
148
      expect = []
149
      for fbase, ecode, rs in [("00succ", 0, HKR_SUCCESS),
150
                               ("10fail", 1, HKR_FAIL),
151
                               ("20inv.", 0, HKR_SKIP),
152
                               ]:
153
        fname = "%s/%s" % (self.ph_dirs[phase], fbase)
154
        f = open(fname, "w")
155
        f.write("#!/bin/sh\nexit %d\n" % ecode)
156
        f.close()
157
        self.torm.append((fname, False))
158
        os.chmod(fname, 0700)
159
        expect.append((self._rname(fname), rs, ""))
160
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
161
                           (True, expect))
162

    
163
  def testOrdering(self):
164
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
165
      expect = []
166
      for fbase in ["10s1",
167
                    "00s0",
168
                    "10sa",
169
                    "80sc",
170
                    "60sd",
171
                    ]:
172
        fname = "%s/%s" % (self.ph_dirs[phase], fbase)
173
        os.symlink("/bin/true", fname)
174
        self.torm.append((fname, False))
175
        expect.append((self._rname(fname), HKR_SUCCESS, ""))
176
      expect.sort()
177
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
178
                           (True, expect))
179

    
180
  def testEnv(self):
181
    """Test environment execution"""
182
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
183
      fbase = "success"
184
      fname = "%s/%s" % (self.ph_dirs[phase], fbase)
185
      os.symlink("/usr/bin/env", fname)
186
      self.torm.append((fname, False))
187
      env_snt = {"PHASE": phase}
188
      env_exp = "PHASE=%s" % phase
189
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, env_snt),
190
                           (True, [(self._rname(fname), HKR_SUCCESS,
191
                                    env_exp)]))
192

    
193

    
194
class TestHooksMaster(unittest.TestCase):
195
  """Testing case for HooksMaster"""
196

    
197
  def _call_false(*args):
198
    """Fake call_hooks_runner function which returns False."""
199
    return False
200

    
201
  @staticmethod
202
  def _call_nodes_false(node_list, hpath, phase, env):
203
    """Fake call_hooks_runner function.
204

205
    @rtype: dict of node -> L{rpc.RpcResult} with an rpc error
206
    @return: rpc failure from all nodes
207

208
    """
209
    return dict([(node, rpc.RpcResult('error', failed=True,
210
                  node=node, call='FakeError')) for node in node_list])
211

    
212
  @staticmethod
213
  def _call_script_fail(node_list, hpath, phase, env):
214
    """Fake call_hooks_runner function.
215

216
    @rtype: dict of node -> L{rpc.RpcResult} with a failed script result
217
    @return: script execution failure from all nodes
218

219
    """
220
    rr = rpc.RpcResult
221
    return dict([(node, rr((True, [("utest", constants.HKR_FAIL, "err")]),
222
                           node=node, call='FakeScriptFail'))
223
                  for node in node_list])
224

    
225
  @staticmethod
226
  def _call_script_succeed(node_list, hpath, phase, env):
227
    """Fake call_hooks_runner function.
228

229
    @rtype: dict of node -> L{rpc.RpcResult} with a successful script result
230
    @return: script execution from all nodes
231

232
    """
233
    rr = rpc.RpcResult
234
    return dict([(node, rr(True, [("utest", constants.HKR_SUCCESS, "ok")],
235
                           node=node, call='FakeScriptOk'))
236
                 for node in node_list])
237

    
238
  def setUp(self):
239
    self.op = opcodes.OpCode()
240
    self.context = FakeContext()
241
    # WARNING: here we pass None as RpcRunner instance since we know
242
    # our usage via HooksMaster will not use lu.rpc
243
    self.lu = FakeLU(FakeProc(), self.op, self.context, None)
244

    
245
  def testTotalFalse(self):
246
    """Test complete rpc failure"""
247
    hm = mcpu.HooksMaster(self._call_false, FakeProc(), self.lu)
248
    self.failUnlessRaises(errors.HooksFailure,
249
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
250
    hm.RunPhase(constants.HOOKS_PHASE_POST)
251

    
252
  def testIndividualFalse(self):
253
    """Test individual node failure"""
254
    hm = mcpu.HooksMaster(self._call_nodes_false, FakeProc(), self.lu)
255
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
256
    #self.failUnlessRaises(errors.HooksFailure,
257
    #                      hm.RunPhase, constants.HOOKS_PHASE_PRE)
258
    hm.RunPhase(constants.HOOKS_PHASE_POST)
259

    
260
  def testScriptFalse(self):
261
    """Test individual rpc failure"""
262
    hm = mcpu.HooksMaster(self._call_script_fail, FakeProc(), self.lu)
263
    self.failUnlessRaises(errors.HooksAbort,
264
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
265
    hm.RunPhase(constants.HOOKS_PHASE_POST)
266

    
267
  def testScriptSucceed(self):
268
    """Test individual rpc failure"""
269
    hm = mcpu.HooksMaster(self._call_script_succeed, FakeProc(), self.lu)
270
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
271
      hm.RunPhase(phase)
272

    
273
if __name__ == '__main__':
274
  unittest.main()