Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.hooks_unittest.py @ 5c983ee5

History | View | Annotate | Download (9.4 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Script for unittesting the hooks module"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
import unittest
26 a8083063 Iustin Pop
import os
27 a8083063 Iustin Pop
import time
28 a8083063 Iustin Pop
import tempfile
29 a8083063 Iustin Pop
import os.path
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import errors
32 a8083063 Iustin Pop
from ganeti import opcodes
33 a8083063 Iustin Pop
from ganeti import mcpu
34 a8083063 Iustin Pop
from ganeti import backend
35 a8083063 Iustin Pop
from ganeti import constants
36 a8083063 Iustin Pop
from ganeti import cmdlib
37 7ccb3074 Guido Trotter
from ganeti import rpc
38 a8083063 Iustin Pop
from ganeti.constants import HKR_SUCCESS, HKR_FAIL, HKR_SKIP
39 a8083063 Iustin Pop
40 c259ce64 Michael Hanselmann
from mocks import FakeConfig, FakeProc, FakeContext
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
class FakeLU(cmdlib.LogicalUnit):
43 a8083063 Iustin Pop
  HPATH = "test"
44 a8083063 Iustin Pop
  def BuildHooksEnv(self):
45 a8083063 Iustin Pop
    return {}, ["localhost"], ["localhost"]
46 a8083063 Iustin Pop
47 a8083063 Iustin Pop
class TestHooksRunner(unittest.TestCase):
48 a8083063 Iustin Pop
  """Testing case for HooksRunner"""
49 a8083063 Iustin Pop
  def setUp(self):
50 a8083063 Iustin Pop
    self.torm = []
51 a8083063 Iustin Pop
    self.tmpdir = tempfile.mkdtemp()
52 a8083063 Iustin Pop
    self.torm.append((self.tmpdir, True))
53 a8083063 Iustin Pop
    self.logdir = tempfile.mkdtemp()
54 a8083063 Iustin Pop
    self.torm.append((self.logdir, True))
55 a8083063 Iustin Pop
    self.hpath = "fake"
56 a8083063 Iustin Pop
    self.ph_dirs = {}
57 a8083063 Iustin Pop
    for i in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
58 a8083063 Iustin Pop
      dname = "%s/%s-%s.d" % (self.tmpdir, self.hpath, i)
59 a8083063 Iustin Pop
      os.mkdir(dname)
60 a8083063 Iustin Pop
      self.torm.append((dname, True))
61 a8083063 Iustin Pop
      self.ph_dirs[i] = dname
62 a8083063 Iustin Pop
    self.hr = backend.HooksRunner(hooks_base_dir=self.tmpdir)
63 a8083063 Iustin Pop
64 a8083063 Iustin Pop
  def tearDown(self):
65 a8083063 Iustin Pop
    self.torm.reverse()
66 a8083063 Iustin Pop
    for path, kind in self.torm:
67 a8083063 Iustin Pop
      if kind:
68 a8083063 Iustin Pop
        os.rmdir(path)
69 a8083063 Iustin Pop
      else:
70 a8083063 Iustin Pop
        os.unlink(path)
71 a8083063 Iustin Pop
72 a8083063 Iustin Pop
  def _rname(self, fname):
73 a8083063 Iustin Pop
    return "/".join(fname.split("/")[-2:])
74 a8083063 Iustin Pop
75 a8083063 Iustin Pop
  def testEmpty(self):
76 a8083063 Iustin Pop
    """Test no hooks"""
77 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
78 d019f8bd Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}), [])
79 a8083063 Iustin Pop
80 a8083063 Iustin Pop
  def testSkipNonExec(self):
81 a8083063 Iustin Pop
    """Test skip non-exec file"""
82 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
83 a8083063 Iustin Pop
      fname = "%s/test" % self.ph_dirs[phase]
84 a8083063 Iustin Pop
      f = open(fname, "w")
85 a8083063 Iustin Pop
      f.close()
86 a8083063 Iustin Pop
      self.torm.append((fname, False))
87 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
88 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_SKIP, "")])
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
  def testSkipInvalidName(self):
91 a8083063 Iustin Pop
    """Test skip script with invalid name"""
92 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
93 a8083063 Iustin Pop
      fname = "%s/a.off" % self.ph_dirs[phase]
94 a8083063 Iustin Pop
      f = open(fname, "w")
95 a8083063 Iustin Pop
      f.write("#!/bin/sh\nexit 0\n")
96 a8083063 Iustin Pop
      f.close()
97 a8083063 Iustin Pop
      os.chmod(fname, 0700)
98 a8083063 Iustin Pop
      self.torm.append((fname, False))
99 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
100 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_SKIP, "")])
101 a8083063 Iustin Pop
102 a8083063 Iustin Pop
  def testSkipDir(self):
103 a8083063 Iustin Pop
    """Test skip directory"""
104 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
105 a8083063 Iustin Pop
      fname = "%s/testdir" % self.ph_dirs[phase]
106 a8083063 Iustin Pop
      os.mkdir(fname)
107 a8083063 Iustin Pop
      self.torm.append((fname, True))
108 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
109 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_SKIP, "")])
110 a8083063 Iustin Pop
111 a8083063 Iustin Pop
  def testSuccess(self):
112 a8083063 Iustin Pop
    """Test success execution"""
113 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
114 a8083063 Iustin Pop
      fname = "%s/success" % self.ph_dirs[phase]
115 a8083063 Iustin Pop
      f = open(fname, "w")
116 a8083063 Iustin Pop
      f.write("#!/bin/sh\nexit 0\n")
117 a8083063 Iustin Pop
      f.close()
118 a8083063 Iustin Pop
      self.torm.append((fname, False))
119 a8083063 Iustin Pop
      os.chmod(fname, 0700)
120 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
121 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_SUCCESS, "")])
122 a8083063 Iustin Pop
123 a8083063 Iustin Pop
  def testSymlink(self):
124 a8083063 Iustin Pop
    """Test running a symlink"""
125 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
126 a8083063 Iustin Pop
      fname = "%s/success" % self.ph_dirs[phase]
127 a8083063 Iustin Pop
      os.symlink("/bin/true", fname)
128 a8083063 Iustin Pop
      self.torm.append((fname, False))
129 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
130 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_SUCCESS, "")])
131 a8083063 Iustin Pop
132 a8083063 Iustin Pop
  def testFail(self):
133 a8083063 Iustin Pop
    """Test success execution"""
134 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
135 a8083063 Iustin Pop
      fname = "%s/success" % self.ph_dirs[phase]
136 a8083063 Iustin Pop
      f = open(fname, "w")
137 a8083063 Iustin Pop
      f.write("#!/bin/sh\nexit 1\n")
138 a8083063 Iustin Pop
      f.close()
139 a8083063 Iustin Pop
      self.torm.append((fname, False))
140 a8083063 Iustin Pop
      os.chmod(fname, 0700)
141 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
142 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_FAIL, "")])
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  def testCombined(self):
145 a8083063 Iustin Pop
    """Test success, failure and skip all in one test"""
146 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
147 a8083063 Iustin Pop
      expect = []
148 a8083063 Iustin Pop
      for fbase, ecode, rs in [("00succ", 0, HKR_SUCCESS),
149 a8083063 Iustin Pop
                               ("10fail", 1, HKR_FAIL),
150 a8083063 Iustin Pop
                               ("20inv.", 0, HKR_SKIP),
151 a8083063 Iustin Pop
                               ]:
152 a8083063 Iustin Pop
        fname = "%s/%s" % (self.ph_dirs[phase], fbase)
153 a8083063 Iustin Pop
        f = open(fname, "w")
154 a8083063 Iustin Pop
        f.write("#!/bin/sh\nexit %d\n" % ecode)
155 a8083063 Iustin Pop
        f.close()
156 a8083063 Iustin Pop
        self.torm.append((fname, False))
157 a8083063 Iustin Pop
        os.chmod(fname, 0700)
158 a8083063 Iustin Pop
        expect.append((self._rname(fname), rs, ""))
159 d019f8bd Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}), expect)
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
  def testOrdering(self):
162 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
163 a8083063 Iustin Pop
      expect = []
164 a8083063 Iustin Pop
      for fbase in ["10s1",
165 a8083063 Iustin Pop
                    "00s0",
166 a8083063 Iustin Pop
                    "10sa",
167 a8083063 Iustin Pop
                    "80sc",
168 a8083063 Iustin Pop
                    "60sd",
169 a8083063 Iustin Pop
                    ]:
170 a8083063 Iustin Pop
        fname = "%s/%s" % (self.ph_dirs[phase], fbase)
171 a8083063 Iustin Pop
        os.symlink("/bin/true", fname)
172 a8083063 Iustin Pop
        self.torm.append((fname, False))
173 a8083063 Iustin Pop
        expect.append((self._rname(fname), HKR_SUCCESS, ""))
174 a8083063 Iustin Pop
      expect.sort()
175 d019f8bd Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}), expect)
176 a8083063 Iustin Pop
177 a8083063 Iustin Pop
  def testEnv(self):
178 a8083063 Iustin Pop
    """Test environment execution"""
179 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
180 a8083063 Iustin Pop
      fbase = "success"
181 a8083063 Iustin Pop
      fname = "%s/%s" % (self.ph_dirs[phase], fbase)
182 a8083063 Iustin Pop
      os.symlink("/usr/bin/env", fname)
183 a8083063 Iustin Pop
      self.torm.append((fname, False))
184 a8083063 Iustin Pop
      env_snt = {"PHASE": phase}
185 7b80424f Iustin Pop
      env_exp = "PHASE=%s" % phase
186 a8083063 Iustin Pop
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, env_snt),
187 d019f8bd Iustin Pop
                           [(self._rname(fname), HKR_SUCCESS, env_exp)])
188 a8083063 Iustin Pop
189 a8083063 Iustin Pop
190 a8083063 Iustin Pop
class TestHooksMaster(unittest.TestCase):
191 a8083063 Iustin Pop
  """Testing case for HooksMaster"""
192 a8083063 Iustin Pop
193 a8083063 Iustin Pop
  def _call_false(*args):
194 a8083063 Iustin Pop
    """Fake call_hooks_runner function which returns False."""
195 a8083063 Iustin Pop
    return False
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
  @staticmethod
198 a8083063 Iustin Pop
  def _call_nodes_false(node_list, hpath, phase, env):
199 a8083063 Iustin Pop
    """Fake call_hooks_runner function.
200 a8083063 Iustin Pop

201 7ccb3074 Guido Trotter
    @rtype: dict of node -> L{rpc.RpcResult} with an rpc error
202 7ccb3074 Guido Trotter
    @return: rpc failure from all nodes
203 a8083063 Iustin Pop

204 a8083063 Iustin Pop
    """
205 7ccb3074 Guido Trotter
    return dict([(node, rpc.RpcResult('error', failed=True,
206 7ccb3074 Guido Trotter
                  node=node, call='FakeError')) for node in node_list])
207 a8083063 Iustin Pop
208 a8083063 Iustin Pop
  @staticmethod
209 a8083063 Iustin Pop
  def _call_script_fail(node_list, hpath, phase, env):
210 a8083063 Iustin Pop
    """Fake call_hooks_runner function.
211 a8083063 Iustin Pop

212 7ccb3074 Guido Trotter
    @rtype: dict of node -> L{rpc.RpcResult} with a failed script result
213 7ccb3074 Guido Trotter
    @return: script execution failure from all nodes
214 a8083063 Iustin Pop

215 a8083063 Iustin Pop
    """
216 3fb4f740 Iustin Pop
    rr = rpc.RpcResult
217 3fb4f740 Iustin Pop
    return dict([(node, rr((True, [("utest", constants.HKR_FAIL, "err")]),
218 3fb4f740 Iustin Pop
                           node=node, call='FakeScriptFail'))
219 3fb4f740 Iustin Pop
                  for node in node_list])
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
  @staticmethod
222 a8083063 Iustin Pop
  def _call_script_succeed(node_list, hpath, phase, env):
223 a8083063 Iustin Pop
    """Fake call_hooks_runner function.
224 a8083063 Iustin Pop

225 7ccb3074 Guido Trotter
    @rtype: dict of node -> L{rpc.RpcResult} with a successful script result
226 7ccb3074 Guido Trotter
    @return: script execution from all nodes
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
    """
229 3fb4f740 Iustin Pop
    rr = rpc.RpcResult
230 3fb4f740 Iustin Pop
    return dict([(node, rr(True, [("utest", constants.HKR_SUCCESS, "ok")],
231 3fb4f740 Iustin Pop
                           node=node, call='FakeScriptOk'))
232 3fb4f740 Iustin Pop
                 for node in node_list])
233 a8083063 Iustin Pop
234 d2525573 Guido Trotter
  def setUp(self):
235 d2525573 Guido Trotter
    self.op = opcodes.OpCode()
236 77b657a3 Guido Trotter
    self.context = FakeContext()
237 72737a7f Iustin Pop
    # WARNING: here we pass None as RpcRunner instance since we know
238 72737a7f Iustin Pop
    # our usage via HooksMaster will not use lu.rpc
239 86d9d3bb Iustin Pop
    self.lu = FakeLU(FakeProc(), self.op, self.context, None)
240 d2525573 Guido Trotter
241 a8083063 Iustin Pop
  def testTotalFalse(self):
242 a8083063 Iustin Pop
    """Test complete rpc failure"""
243 4b5e8271 Iustin Pop
    hm = mcpu.HooksMaster(self._call_false, self.lu)
244 a8083063 Iustin Pop
    self.failUnlessRaises(errors.HooksFailure,
245 a8083063 Iustin Pop
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
246 a8083063 Iustin Pop
    hm.RunPhase(constants.HOOKS_PHASE_POST)
247 a8083063 Iustin Pop
248 a8083063 Iustin Pop
  def testIndividualFalse(self):
249 2395c322 Iustin Pop
    """Test individual node failure"""
250 4b5e8271 Iustin Pop
    hm = mcpu.HooksMaster(self._call_nodes_false, self.lu)
251 2395c322 Iustin Pop
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
252 2395c322 Iustin Pop
    #self.failUnlessRaises(errors.HooksFailure,
253 2395c322 Iustin Pop
    #                      hm.RunPhase, constants.HOOKS_PHASE_PRE)
254 a8083063 Iustin Pop
    hm.RunPhase(constants.HOOKS_PHASE_POST)
255 a8083063 Iustin Pop
256 a8083063 Iustin Pop
  def testScriptFalse(self):
257 a8083063 Iustin Pop
    """Test individual rpc failure"""
258 4b5e8271 Iustin Pop
    hm = mcpu.HooksMaster(self._call_script_fail, self.lu)
259 a8083063 Iustin Pop
    self.failUnlessRaises(errors.HooksAbort,
260 a8083063 Iustin Pop
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
261 a8083063 Iustin Pop
    hm.RunPhase(constants.HOOKS_PHASE_POST)
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
  def testScriptSucceed(self):
264 a8083063 Iustin Pop
    """Test individual rpc failure"""
265 4b5e8271 Iustin Pop
    hm = mcpu.HooksMaster(self._call_script_succeed, self.lu)
266 a8083063 Iustin Pop
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
267 a8083063 Iustin Pop
      hm.RunPhase(phase)
268 a8083063 Iustin Pop
269 a8083063 Iustin Pop
if __name__ == '__main__':
270 a8083063 Iustin Pop
  unittest.main()