Revision 6bb65e3a
b/lib/backend.py | ||
---|---|---|
40 | 40 |
import stat |
41 | 41 |
import errno |
42 | 42 |
import re |
43 |
import subprocess |
|
44 | 43 |
import random |
45 | 44 |
import logging |
46 | 45 |
import tempfile |
... | ... | |
2666 | 2665 |
else: |
2667 | 2666 |
_Fail("Unknown hooks phase '%s'", phase) |
2668 | 2667 |
|
2669 |
rr = [] |
|
2670 | 2668 |
|
2671 | 2669 |
subdir = "%s-%s.d" % (hpath, suffix) |
2672 | 2670 |
dir_name = "%s/%s" % (self._BASE_DIR, subdir) |
2673 |
try: |
|
2674 |
dir_contents = utils.ListVisibleFiles(dir_name) |
|
2675 |
except OSError: |
|
2676 |
# FIXME: must log output in case of failures |
|
2677 |
return rr |
|
2678 |
|
|
2679 |
# we use the standard python sort order, |
|
2680 |
# so 00name is the recommended naming scheme |
|
2681 |
dir_contents.sort() |
|
2682 |
for relname in dir_contents: |
|
2683 |
fname = os.path.join(dir_name, relname) |
|
2684 |
if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and |
|
2685 |
constants.EXT_PLUGIN_MASK.match(relname) is not None): |
|
2671 |
runparts_results = utils.RunParts(dir_name, env=env, reset_env=True) |
|
2672 |
|
|
2673 |
results = [] |
|
2674 |
for (relname, relstatus, runresult) in runparts_results: |
|
2675 |
if relstatus == constants.RUNPARTS_SKIP: |
|
2686 | 2676 |
rrval = constants.HKR_SKIP |
2687 | 2677 |
output = "" |
2688 |
else: |
|
2689 |
try: |
|
2690 |
result = utils.RunCmd([fname], env=env, reset_env=True) |
|
2691 |
except (OpExecError, EnvironmentError), err: |
|
2678 |
elif relstatus == constants.RUNPARTS_ERR: |
|
2679 |
rrval = constants.HKR_FAIL |
|
2680 |
output = "Hook script execution error: %s" % runresult |
|
2681 |
elif relstatus == constants.RUNPARTS_RUN: |
|
2682 |
if runresult.failed: |
|
2692 | 2683 |
rrval = constants.HKR_FAIL |
2693 |
output = "Hook script error: %s" % str(err) |
|
2694 | 2684 |
else: |
2695 |
if result.failed: |
|
2696 |
rrval = constants.HKR_FAIL |
|
2697 |
else: |
|
2698 |
rrval = constants.HKR_SUCCESS |
|
2699 |
output = utils.SafeEncode(result.output.strip()) |
|
2700 |
rr.append(("%s/%s" % (subdir, relname), rrval, output)) |
|
2701 |
|
|
2702 |
return rr |
|
2685 |
rrval = constants.HKR_SUCCESS |
|
2686 |
output = utils.SafeEncode(runresult.output.strip()) |
|
2687 |
results.append(("%s/%s" % (subdir, relname), rrval, output)) |
|
2688 |
|
|
2689 |
return results |
|
2703 | 2690 |
|
2704 | 2691 |
|
2705 | 2692 |
class IAllocatorRunner(object): |
b/lib/constants.py | ||
---|---|---|
335 | 335 |
DEFAULT_SHUTDOWN_TIMEOUT = 120 |
336 | 336 |
NODE_MAX_CLOCK_SKEW = 150 |
337 | 337 |
|
338 |
# runparts results |
|
339 |
(RUNPARTS_SKIP, |
|
340 |
RUNPARTS_RUN, |
|
341 |
RUNPARTS_ERR) = range(3) |
|
342 |
|
|
343 |
RUNPARTS_STATUS = frozenset([RUNPARTS_SKIP, RUNPARTS_RUN, RUNPARTS_ERR]) |
|
344 |
|
|
338 | 345 |
# RPC constants |
339 | 346 |
(RPC_ENCODING_NONE, |
340 | 347 |
RPC_ENCODING_ZLIB_BASE64) = range(2) |
b/lib/utils.py | ||
---|---|---|
288 | 288 |
return status |
289 | 289 |
|
290 | 290 |
|
291 |
def RunParts(dir_name, env=None, reset_env=False): |
|
292 |
"""Run Scripts or programs in a directory |
|
293 |
|
|
294 |
@type dir_name: string |
|
295 |
@param dir_name: absolute path to a directory |
|
296 |
@type env: dict |
|
297 |
@param env: The environment to use |
|
298 |
@type reset_env: boolean |
|
299 |
@param reset_env: whether to reset or keep the default os environment |
|
300 |
@rtype: list of tuples |
|
301 |
@return: list of (name, (one of RUNDIR_STATUS), RunResult) |
|
302 |
|
|
303 |
""" |
|
304 |
rr = [] |
|
305 |
|
|
306 |
try: |
|
307 |
dir_contents = ListVisibleFiles(dir_name) |
|
308 |
except OSError, err: |
|
309 |
logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err) |
|
310 |
return rr |
|
311 |
|
|
312 |
for relname in sorted(dir_contents): |
|
313 |
fname = os.path.join(dir_name, relname) |
|
314 |
if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and |
|
315 |
constants.EXT_PLUGIN_MASK.match(relname) is not None): |
|
316 |
rr.append((relname, constants.RUNPARTS_SKIP, None)) |
|
317 |
else: |
|
318 |
try: |
|
319 |
result = RunCmd([fname], env=env, reset_env=reset_env) |
|
320 |
except Exception, err: # pylint: disable-msg=W0703 |
|
321 |
rr.append((relname, constants.RUNPARTS_ERR, str(err))) |
|
322 |
else: |
|
323 |
rr.append((relname, constants.RUNPARTS_RUN, result)) |
|
324 |
|
|
325 |
return rr |
|
326 |
|
|
327 |
|
|
291 | 328 |
def RemoveFile(filename): |
292 | 329 |
"""Remove a file ignoring some errors. |
293 | 330 |
|
b/test/ganeti.utils_unittest.py | ||
---|---|---|
27 | 27 |
import tempfile |
28 | 28 |
import os.path |
29 | 29 |
import os |
30 |
import stat |
|
30 | 31 |
import md5 |
31 | 32 |
import signal |
32 | 33 |
import socket |
... | ... | |
46 | 47 |
ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \ |
47 | 48 |
SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \ |
48 | 49 |
TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \ |
49 |
UnescapeAndSplit |
|
50 |
UnescapeAndSplit, RunParts
|
|
50 | 51 |
|
51 | 52 |
from ganeti.errors import LockError, UnitParseError, GenericError, \ |
52 | 53 |
ProgrammerError |
... | ... | |
236 | 237 |
self.failUnlessEqual(RunCmd(["env"], reset_env=True).stdout.strip(), "") |
237 | 238 |
|
238 | 239 |
|
240 |
class TestRunParts(unittest.TestCase): |
|
241 |
"""Testing case for the RunParts function""" |
|
242 |
|
|
243 |
def setUp(self): |
|
244 |
self.rundir = tempfile.mkdtemp(prefix="ganeti-test", suffix=".tmp") |
|
245 |
|
|
246 |
def tearDown(self): |
|
247 |
shutil.rmtree(self.rundir) |
|
248 |
|
|
249 |
def testEmpty(self): |
|
250 |
"""Test on an empty dir""" |
|
251 |
self.failUnlessEqual(RunParts(self.rundir, reset_env=True), []) |
|
252 |
|
|
253 |
def testSkipWrongName(self): |
|
254 |
"""Test that wrong files are skipped""" |
|
255 |
fname = os.path.join(self.rundir, "00test.dot") |
|
256 |
utils.WriteFile(fname, data="") |
|
257 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
|
258 |
relname = os.path.basename(fname) |
|
259 |
self.failUnlessEqual(RunParts(self.rundir, reset_env=True), |
|
260 |
[(relname, constants.RUNPARTS_SKIP, None)]) |
|
261 |
|
|
262 |
def testSkipNonExec(self): |
|
263 |
"""Test that non executable files are skipped""" |
|
264 |
fname = os.path.join(self.rundir, "00test") |
|
265 |
utils.WriteFile(fname, data="") |
|
266 |
relname = os.path.basename(fname) |
|
267 |
self.failUnlessEqual(RunParts(self.rundir, reset_env=True), |
|
268 |
[(relname, constants.RUNPARTS_SKIP, None)]) |
|
269 |
|
|
270 |
def testError(self): |
|
271 |
"""Test error on a broken executable""" |
|
272 |
fname = os.path.join(self.rundir, "00test") |
|
273 |
utils.WriteFile(fname, data="") |
|
274 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
|
275 |
(relname, status, error) = RunParts(self.rundir, reset_env=True)[0] |
|
276 |
self.failUnlessEqual(relname, os.path.basename(fname)) |
|
277 |
self.failUnlessEqual(status, constants.RUNPARTS_ERR) |
|
278 |
self.failUnless(error) |
|
279 |
|
|
280 |
def testSorted(self): |
|
281 |
"""Test executions are sorted""" |
|
282 |
files = [] |
|
283 |
files.append(os.path.join(self.rundir, "64test")) |
|
284 |
files.append(os.path.join(self.rundir, "00test")) |
|
285 |
files.append(os.path.join(self.rundir, "42test")) |
|
286 |
|
|
287 |
for fname in files: |
|
288 |
utils.WriteFile(fname, data="") |
|
289 |
|
|
290 |
results = RunParts(self.rundir, reset_env=True) |
|
291 |
|
|
292 |
for fname in sorted(files): |
|
293 |
self.failUnlessEqual(os.path.basename(fname), results.pop(0)[0]) |
|
294 |
|
|
295 |
def testOk(self): |
|
296 |
"""Test correct execution""" |
|
297 |
fname = os.path.join(self.rundir, "00test") |
|
298 |
utils.WriteFile(fname, data="#!/bin/sh\n\necho -n ciao") |
|
299 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
|
300 |
(relname, status, runresult) = RunParts(self.rundir, reset_env=True)[0] |
|
301 |
self.failUnlessEqual(relname, os.path.basename(fname)) |
|
302 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN) |
|
303 |
self.failUnlessEqual(runresult.stdout, "ciao") |
|
304 |
|
|
305 |
def testRunFail(self): |
|
306 |
"""Test correct execution, with run failure""" |
|
307 |
fname = os.path.join(self.rundir, "00test") |
|
308 |
utils.WriteFile(fname, data="#!/bin/sh\n\nexit 1") |
|
309 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
|
310 |
(relname, status, runresult) = RunParts(self.rundir, reset_env=True)[0] |
|
311 |
self.failUnlessEqual(relname, os.path.basename(fname)) |
|
312 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN) |
|
313 |
self.failUnlessEqual(runresult.exit_code, 1) |
|
314 |
self.failUnless(runresult.failed) |
|
315 |
|
|
316 |
def testRunMix(self): |
|
317 |
files = [] |
|
318 |
files.append(os.path.join(self.rundir, "00test")) |
|
319 |
files.append(os.path.join(self.rundir, "42test")) |
|
320 |
files.append(os.path.join(self.rundir, "64test")) |
|
321 |
files.append(os.path.join(self.rundir, "99test")) |
|
322 |
|
|
323 |
files.sort() |
|
324 |
|
|
325 |
# 1st has errors in execution |
|
326 |
utils.WriteFile(files[0], data="#!/bin/sh\n\nexit 1") |
|
327 |
os.chmod(files[0], stat.S_IREAD | stat.S_IEXEC) |
|
328 |
|
|
329 |
# 2nd is skipped |
|
330 |
utils.WriteFile(files[1], data="") |
|
331 |
|
|
332 |
# 3rd cannot execute properly |
|
333 |
utils.WriteFile(files[2], data="") |
|
334 |
os.chmod(files[2], stat.S_IREAD | stat.S_IEXEC) |
|
335 |
|
|
336 |
# 4th execs |
|
337 |
utils.WriteFile(files[3], data="#!/bin/sh\n\necho -n ciao") |
|
338 |
os.chmod(files[3], stat.S_IREAD | stat.S_IEXEC) |
|
339 |
|
|
340 |
results = RunParts(self.rundir, reset_env=True) |
|
341 |
|
|
342 |
(relname, status, runresult) = results[0] |
|
343 |
self.failUnlessEqual(relname, os.path.basename(files[0])) |
|
344 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN) |
|
345 |
self.failUnlessEqual(runresult.exit_code, 1) |
|
346 |
self.failUnless(runresult.failed) |
|
347 |
|
|
348 |
(relname, status, runresult) = results[1] |
|
349 |
self.failUnlessEqual(relname, os.path.basename(files[1])) |
|
350 |
self.failUnlessEqual(status, constants.RUNPARTS_SKIP) |
|
351 |
self.failUnlessEqual(runresult, None) |
|
352 |
|
|
353 |
(relname, status, runresult) = results[2] |
|
354 |
self.failUnlessEqual(relname, os.path.basename(files[2])) |
|
355 |
self.failUnlessEqual(status, constants.RUNPARTS_ERR) |
|
356 |
self.failUnless(runresult) |
|
357 |
|
|
358 |
(relname, status, runresult) = results[3] |
|
359 |
self.failUnlessEqual(relname, os.path.basename(files[3])) |
|
360 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN) |
|
361 |
self.failUnlessEqual(runresult.output, "ciao") |
|
362 |
self.failUnlessEqual(runresult.exit_code, 0) |
|
363 |
self.failUnless(not runresult.failed) |
|
364 |
|
|
365 |
|
|
239 | 366 |
class TestRemoveFile(unittest.TestCase): |
240 | 367 |
"""Test case for the RemoveFile function""" |
241 | 368 |
|
Also available in: Unified diff