root / test / ganeti.utils_unittest.py @ 17b97ab3
History | View | Annotate | Download (30.7 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2006, 2007, 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 unittesting the utils module"""
|
23 |
|
24 |
import errno |
25 |
import fcntl |
26 |
import glob |
27 |
import os |
28 |
import os.path |
29 |
import re |
30 |
import shutil |
31 |
import signal |
32 |
import socket |
33 |
import stat |
34 |
import tempfile |
35 |
import time |
36 |
import unittest |
37 |
import warnings |
38 |
import random |
39 |
import operator |
40 |
|
41 |
import testutils |
42 |
from ganeti import constants |
43 |
from ganeti import compat |
44 |
from ganeti import utils |
45 |
from ganeti import errors |
46 |
from ganeti.utils import RunCmd, \ |
47 |
FirstFree, \
|
48 |
RunParts
|
49 |
|
50 |
|
51 |
class TestIsProcessAlive(unittest.TestCase): |
52 |
"""Testing case for IsProcessAlive"""
|
53 |
|
54 |
def testExists(self): |
55 |
mypid = os.getpid() |
56 |
self.assert_(utils.IsProcessAlive(mypid), "can't find myself running") |
57 |
|
58 |
def testNotExisting(self): |
59 |
pid_non_existing = os.fork() |
60 |
if pid_non_existing == 0: |
61 |
os._exit(0)
|
62 |
elif pid_non_existing < 0: |
63 |
raise SystemError("can't fork") |
64 |
os.waitpid(pid_non_existing, 0)
|
65 |
self.assertFalse(utils.IsProcessAlive(pid_non_existing),
|
66 |
"nonexisting process detected")
|
67 |
|
68 |
|
69 |
class TestGetProcStatusPath(unittest.TestCase): |
70 |
def test(self): |
71 |
self.assert_("/1234/" in utils._GetProcStatusPath(1234)) |
72 |
self.assertNotEqual(utils._GetProcStatusPath(1), |
73 |
utils._GetProcStatusPath(2))
|
74 |
|
75 |
|
76 |
class TestIsProcessHandlingSignal(unittest.TestCase): |
77 |
def setUp(self): |
78 |
self.tmpdir = tempfile.mkdtemp()
|
79 |
|
80 |
def tearDown(self): |
81 |
shutil.rmtree(self.tmpdir)
|
82 |
|
83 |
def testParseSigsetT(self): |
84 |
self.assertEqual(len(utils._ParseSigsetT("0")), 0) |
85 |
self.assertEqual(utils._ParseSigsetT("1"), set([1])) |
86 |
self.assertEqual(utils._ParseSigsetT("1000a"), set([2, 4, 17])) |
87 |
self.assertEqual(utils._ParseSigsetT("810002"), set([2, 17, 24, ])) |
88 |
self.assertEqual(utils._ParseSigsetT("0000000180000202"), |
89 |
set([2, 10, 32, 33])) |
90 |
self.assertEqual(utils._ParseSigsetT("0000000180000002"), |
91 |
set([2, 32, 33])) |
92 |
self.assertEqual(utils._ParseSigsetT("0000000188000002"), |
93 |
set([2, 28, 32, 33])) |
94 |
self.assertEqual(utils._ParseSigsetT("000000004b813efb"), |
95 |
set([1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 17, |
96 |
24, 25, 26, 28, 31])) |
97 |
self.assertEqual(utils._ParseSigsetT("ffffff"), set(range(1, 25))) |
98 |
|
99 |
def testGetProcStatusField(self): |
100 |
for field in ["SigCgt", "Name", "FDSize"]: |
101 |
for value in ["", "0", "cat", " 1234 KB"]: |
102 |
pstatus = "\n".join([
|
103 |
"VmPeak: 999 kB",
|
104 |
"%s: %s" % (field, value),
|
105 |
"TracerPid: 0",
|
106 |
]) |
107 |
result = utils._GetProcStatusField(pstatus, field) |
108 |
self.assertEqual(result, value.strip())
|
109 |
|
110 |
def test(self): |
111 |
sp = utils.PathJoin(self.tmpdir, "status") |
112 |
|
113 |
utils.WriteFile(sp, data="\n".join([
|
114 |
"Name: bash",
|
115 |
"State: S (sleeping)",
|
116 |
"SleepAVG: 98%",
|
117 |
"Pid: 22250",
|
118 |
"PPid: 10858",
|
119 |
"TracerPid: 0",
|
120 |
"SigBlk: 0000000000010000",
|
121 |
"SigIgn: 0000000000384004",
|
122 |
"SigCgt: 000000004b813efb",
|
123 |
"CapEff: 0000000000000000",
|
124 |
])) |
125 |
|
126 |
self.assert_(utils.IsProcessHandlingSignal(1234, 10, status_path=sp)) |
127 |
|
128 |
def testNoSigCgt(self): |
129 |
sp = utils.PathJoin(self.tmpdir, "status") |
130 |
|
131 |
utils.WriteFile(sp, data="\n".join([
|
132 |
"Name: bash",
|
133 |
])) |
134 |
|
135 |
self.assertRaises(RuntimeError, utils.IsProcessHandlingSignal, |
136 |
1234, 10, status_path=sp) |
137 |
|
138 |
def testNoSuchFile(self): |
139 |
sp = utils.PathJoin(self.tmpdir, "notexist") |
140 |
|
141 |
self.assertFalse(utils.IsProcessHandlingSignal(1234, 10, status_path=sp)) |
142 |
|
143 |
@staticmethod
|
144 |
def _TestRealProcess(): |
145 |
signal.signal(signal.SIGUSR1, signal.SIG_DFL) |
146 |
if utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
|
147 |
raise Exception("SIGUSR1 is handled when it should not be") |
148 |
|
149 |
signal.signal(signal.SIGUSR1, lambda signum, frame: None) |
150 |
if not utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1): |
151 |
raise Exception("SIGUSR1 is not handled when it should be") |
152 |
|
153 |
signal.signal(signal.SIGUSR1, signal.SIG_IGN) |
154 |
if utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
|
155 |
raise Exception("SIGUSR1 is not handled when it should be") |
156 |
|
157 |
signal.signal(signal.SIGUSR1, signal.SIG_DFL) |
158 |
if utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
|
159 |
raise Exception("SIGUSR1 is handled when it should not be") |
160 |
|
161 |
return True |
162 |
|
163 |
def testRealProcess(self): |
164 |
self.assert_(utils.RunInSeparateProcess(self._TestRealProcess)) |
165 |
|
166 |
|
167 |
class TestRunCmd(testutils.GanetiTestCase): |
168 |
"""Testing case for the RunCmd function"""
|
169 |
|
170 |
def setUp(self): |
171 |
testutils.GanetiTestCase.setUp(self)
|
172 |
self.magic = time.ctime() + " ganeti test" |
173 |
self.fname = self._CreateTempFile() |
174 |
self.fifo_tmpdir = tempfile.mkdtemp()
|
175 |
self.fifo_file = os.path.join(self.fifo_tmpdir, "ganeti_test_fifo") |
176 |
os.mkfifo(self.fifo_file)
|
177 |
|
178 |
def tearDown(self): |
179 |
shutil.rmtree(self.fifo_tmpdir)
|
180 |
testutils.GanetiTestCase.tearDown(self)
|
181 |
|
182 |
def testOk(self): |
183 |
"""Test successful exit code"""
|
184 |
result = RunCmd("/bin/sh -c 'exit 0'")
|
185 |
self.assertEqual(result.exit_code, 0) |
186 |
self.assertEqual(result.output, "") |
187 |
|
188 |
def testFail(self): |
189 |
"""Test fail exit code"""
|
190 |
result = RunCmd("/bin/sh -c 'exit 1'")
|
191 |
self.assertEqual(result.exit_code, 1) |
192 |
self.assertEqual(result.output, "") |
193 |
|
194 |
def testStdout(self): |
195 |
"""Test standard output"""
|
196 |
cmd = 'echo -n "%s"' % self.magic |
197 |
result = RunCmd("/bin/sh -c '%s'" % cmd)
|
198 |
self.assertEqual(result.stdout, self.magic) |
199 |
result = RunCmd("/bin/sh -c '%s'" % cmd, output=self.fname) |
200 |
self.assertEqual(result.output, "") |
201 |
self.assertFileContent(self.fname, self.magic) |
202 |
|
203 |
def testStderr(self): |
204 |
"""Test standard error"""
|
205 |
cmd = 'echo -n "%s"' % self.magic |
206 |
result = RunCmd("/bin/sh -c '%s' 1>&2" % cmd)
|
207 |
self.assertEqual(result.stderr, self.magic) |
208 |
result = RunCmd("/bin/sh -c '%s' 1>&2" % cmd, output=self.fname) |
209 |
self.assertEqual(result.output, "") |
210 |
self.assertFileContent(self.fname, self.magic) |
211 |
|
212 |
def testCombined(self): |
213 |
"""Test combined output"""
|
214 |
cmd = 'echo -n "A%s"; echo -n "B%s" 1>&2' % (self.magic, self.magic) |
215 |
expected = "A" + self.magic + "B" + self.magic |
216 |
result = RunCmd("/bin/sh -c '%s'" % cmd)
|
217 |
self.assertEqual(result.output, expected)
|
218 |
result = RunCmd("/bin/sh -c '%s'" % cmd, output=self.fname) |
219 |
self.assertEqual(result.output, "") |
220 |
self.assertFileContent(self.fname, expected) |
221 |
|
222 |
def testSignal(self): |
223 |
"""Test signal"""
|
224 |
result = RunCmd(["python", "-c", "import os; os.kill(os.getpid(), 15)"]) |
225 |
self.assertEqual(result.signal, 15) |
226 |
self.assertEqual(result.output, "") |
227 |
|
228 |
def testTimeoutClean(self): |
229 |
cmd = "trap 'exit 0' TERM; read < %s" % self.fifo_file |
230 |
result = RunCmd(["/bin/sh", "-c", cmd], timeout=0.2) |
231 |
self.assertEqual(result.exit_code, 0) |
232 |
|
233 |
def testTimeoutKill(self): |
234 |
cmd = ["/bin/sh", "-c", "trap '' TERM; read < %s" % self.fifo_file] |
235 |
timeout = 0.2
|
236 |
out, err, status, ta = utils._RunCmdPipe(cmd, {}, False, "/", False, |
237 |
timeout, _linger_timeout=0.2)
|
238 |
self.assert_(status < 0) |
239 |
self.assertEqual(-status, signal.SIGKILL)
|
240 |
|
241 |
def testTimeoutOutputAfterTerm(self): |
242 |
cmd = "trap 'echo sigtermed; exit 1' TERM; read < %s" % self.fifo_file |
243 |
result = RunCmd(["/bin/sh", "-c", cmd], timeout=0.2) |
244 |
self.assert_(result.failed)
|
245 |
self.assertEqual(result.stdout, "sigtermed\n") |
246 |
|
247 |
def testListRun(self): |
248 |
"""Test list runs"""
|
249 |
result = RunCmd(["true"])
|
250 |
self.assertEqual(result.signal, None) |
251 |
self.assertEqual(result.exit_code, 0) |
252 |
result = RunCmd(["/bin/sh", "-c", "exit 1"]) |
253 |
self.assertEqual(result.signal, None) |
254 |
self.assertEqual(result.exit_code, 1) |
255 |
result = RunCmd(["echo", "-n", self.magic]) |
256 |
self.assertEqual(result.signal, None) |
257 |
self.assertEqual(result.exit_code, 0) |
258 |
self.assertEqual(result.stdout, self.magic) |
259 |
|
260 |
def testFileEmptyOutput(self): |
261 |
"""Test file output"""
|
262 |
result = RunCmd(["true"], output=self.fname) |
263 |
self.assertEqual(result.signal, None) |
264 |
self.assertEqual(result.exit_code, 0) |
265 |
self.assertFileContent(self.fname, "") |
266 |
|
267 |
def testLang(self): |
268 |
"""Test locale environment"""
|
269 |
old_env = os.environ.copy() |
270 |
try:
|
271 |
os.environ["LANG"] = "en_US.UTF-8" |
272 |
os.environ["LC_ALL"] = "en_US.UTF-8" |
273 |
result = RunCmd(["locale"])
|
274 |
for line in result.output.splitlines(): |
275 |
key, value = line.split("=", 1) |
276 |
# Ignore these variables, they're overridden by LC_ALL
|
277 |
if key == "LANG" or key == "LANGUAGE": |
278 |
continue
|
279 |
self.failIf(value and value != "C" and value != '"C"', |
280 |
"Variable %s is set to the invalid value '%s'" % (key, value))
|
281 |
finally:
|
282 |
os.environ = old_env |
283 |
|
284 |
def testDefaultCwd(self): |
285 |
"""Test default working directory"""
|
286 |
self.failUnlessEqual(RunCmd(["pwd"]).stdout.strip(), "/") |
287 |
|
288 |
def testCwd(self): |
289 |
"""Test default working directory"""
|
290 |
self.failUnlessEqual(RunCmd(["pwd"], cwd="/").stdout.strip(), "/") |
291 |
self.failUnlessEqual(RunCmd(["pwd"], cwd="/tmp").stdout.strip(), "/tmp") |
292 |
cwd = os.getcwd() |
293 |
self.failUnlessEqual(RunCmd(["pwd"], cwd=cwd).stdout.strip(), cwd) |
294 |
|
295 |
def testResetEnv(self): |
296 |
"""Test environment reset functionality"""
|
297 |
self.failUnlessEqual(RunCmd(["env"], reset_env=True).stdout.strip(), "") |
298 |
self.failUnlessEqual(RunCmd(["env"], reset_env=True, |
299 |
env={"FOO": "bar",}).stdout.strip(), "FOO=bar") |
300 |
|
301 |
def testNoFork(self): |
302 |
"""Test that nofork raise an error"""
|
303 |
self.assertFalse(utils._no_fork)
|
304 |
utils.DisableFork() |
305 |
try:
|
306 |
self.assertTrue(utils._no_fork)
|
307 |
self.assertRaises(errors.ProgrammerError, RunCmd, ["true"]) |
308 |
finally:
|
309 |
utils._no_fork = False
|
310 |
|
311 |
def testWrongParams(self): |
312 |
"""Test wrong parameters"""
|
313 |
self.assertRaises(errors.ProgrammerError, RunCmd, ["true"], |
314 |
output="/dev/null", interactive=True) |
315 |
|
316 |
|
317 |
class TestRunParts(testutils.GanetiTestCase): |
318 |
"""Testing case for the RunParts function"""
|
319 |
|
320 |
def setUp(self): |
321 |
self.rundir = tempfile.mkdtemp(prefix="ganeti-test", suffix=".tmp") |
322 |
|
323 |
def tearDown(self): |
324 |
shutil.rmtree(self.rundir)
|
325 |
|
326 |
def testEmpty(self): |
327 |
"""Test on an empty dir"""
|
328 |
self.failUnlessEqual(RunParts(self.rundir, reset_env=True), []) |
329 |
|
330 |
def testSkipWrongName(self): |
331 |
"""Test that wrong files are skipped"""
|
332 |
fname = os.path.join(self.rundir, "00test.dot") |
333 |
utils.WriteFile(fname, data="")
|
334 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
335 |
relname = os.path.basename(fname) |
336 |
self.failUnlessEqual(RunParts(self.rundir, reset_env=True), |
337 |
[(relname, constants.RUNPARTS_SKIP, None)])
|
338 |
|
339 |
def testSkipNonExec(self): |
340 |
"""Test that non executable files are skipped"""
|
341 |
fname = os.path.join(self.rundir, "00test") |
342 |
utils.WriteFile(fname, data="")
|
343 |
relname = os.path.basename(fname) |
344 |
self.failUnlessEqual(RunParts(self.rundir, reset_env=True), |
345 |
[(relname, constants.RUNPARTS_SKIP, None)])
|
346 |
|
347 |
def testError(self): |
348 |
"""Test error on a broken executable"""
|
349 |
fname = os.path.join(self.rundir, "00test") |
350 |
utils.WriteFile(fname, data="")
|
351 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
352 |
(relname, status, error) = RunParts(self.rundir, reset_env=True)[0] |
353 |
self.failUnlessEqual(relname, os.path.basename(fname))
|
354 |
self.failUnlessEqual(status, constants.RUNPARTS_ERR)
|
355 |
self.failUnless(error)
|
356 |
|
357 |
def testSorted(self): |
358 |
"""Test executions are sorted"""
|
359 |
files = [] |
360 |
files.append(os.path.join(self.rundir, "64test")) |
361 |
files.append(os.path.join(self.rundir, "00test")) |
362 |
files.append(os.path.join(self.rundir, "42test")) |
363 |
|
364 |
for fname in files: |
365 |
utils.WriteFile(fname, data="")
|
366 |
|
367 |
results = RunParts(self.rundir, reset_env=True) |
368 |
|
369 |
for fname in sorted(files): |
370 |
self.failUnlessEqual(os.path.basename(fname), results.pop(0)[0]) |
371 |
|
372 |
def testOk(self): |
373 |
"""Test correct execution"""
|
374 |
fname = os.path.join(self.rundir, "00test") |
375 |
utils.WriteFile(fname, data="#!/bin/sh\n\necho -n ciao")
|
376 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
377 |
(relname, status, runresult) = RunParts(self.rundir, reset_env=True)[0] |
378 |
self.failUnlessEqual(relname, os.path.basename(fname))
|
379 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN)
|
380 |
self.failUnlessEqual(runresult.stdout, "ciao") |
381 |
|
382 |
def testRunFail(self): |
383 |
"""Test correct execution, with run failure"""
|
384 |
fname = os.path.join(self.rundir, "00test") |
385 |
utils.WriteFile(fname, data="#!/bin/sh\n\nexit 1")
|
386 |
os.chmod(fname, stat.S_IREAD | stat.S_IEXEC) |
387 |
(relname, status, runresult) = RunParts(self.rundir, reset_env=True)[0] |
388 |
self.failUnlessEqual(relname, os.path.basename(fname))
|
389 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN)
|
390 |
self.failUnlessEqual(runresult.exit_code, 1) |
391 |
self.failUnless(runresult.failed)
|
392 |
|
393 |
def testRunMix(self): |
394 |
files = [] |
395 |
files.append(os.path.join(self.rundir, "00test")) |
396 |
files.append(os.path.join(self.rundir, "42test")) |
397 |
files.append(os.path.join(self.rundir, "64test")) |
398 |
files.append(os.path.join(self.rundir, "99test")) |
399 |
|
400 |
files.sort() |
401 |
|
402 |
# 1st has errors in execution
|
403 |
utils.WriteFile(files[0], data="#!/bin/sh\n\nexit 1") |
404 |
os.chmod(files[0], stat.S_IREAD | stat.S_IEXEC)
|
405 |
|
406 |
# 2nd is skipped
|
407 |
utils.WriteFile(files[1], data="") |
408 |
|
409 |
# 3rd cannot execute properly
|
410 |
utils.WriteFile(files[2], data="") |
411 |
os.chmod(files[2], stat.S_IREAD | stat.S_IEXEC)
|
412 |
|
413 |
# 4th execs
|
414 |
utils.WriteFile(files[3], data="#!/bin/sh\n\necho -n ciao") |
415 |
os.chmod(files[3], stat.S_IREAD | stat.S_IEXEC)
|
416 |
|
417 |
results = RunParts(self.rundir, reset_env=True) |
418 |
|
419 |
(relname, status, runresult) = results[0]
|
420 |
self.failUnlessEqual(relname, os.path.basename(files[0])) |
421 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN)
|
422 |
self.failUnlessEqual(runresult.exit_code, 1) |
423 |
self.failUnless(runresult.failed)
|
424 |
|
425 |
(relname, status, runresult) = results[1]
|
426 |
self.failUnlessEqual(relname, os.path.basename(files[1])) |
427 |
self.failUnlessEqual(status, constants.RUNPARTS_SKIP)
|
428 |
self.failUnlessEqual(runresult, None) |
429 |
|
430 |
(relname, status, runresult) = results[2]
|
431 |
self.failUnlessEqual(relname, os.path.basename(files[2])) |
432 |
self.failUnlessEqual(status, constants.RUNPARTS_ERR)
|
433 |
self.failUnless(runresult)
|
434 |
|
435 |
(relname, status, runresult) = results[3]
|
436 |
self.failUnlessEqual(relname, os.path.basename(files[3])) |
437 |
self.failUnlessEqual(status, constants.RUNPARTS_RUN)
|
438 |
self.failUnlessEqual(runresult.output, "ciao") |
439 |
self.failUnlessEqual(runresult.exit_code, 0) |
440 |
self.failUnless(not runresult.failed) |
441 |
|
442 |
def testMissingDirectory(self): |
443 |
nosuchdir = utils.PathJoin(self.rundir, "no/such/directory") |
444 |
self.assertEqual(RunParts(nosuchdir), [])
|
445 |
|
446 |
|
447 |
class TestStartDaemon(testutils.GanetiTestCase): |
448 |
def setUp(self): |
449 |
self.tmpdir = tempfile.mkdtemp(prefix="ganeti-test") |
450 |
self.tmpfile = os.path.join(self.tmpdir, "test") |
451 |
|
452 |
def tearDown(self): |
453 |
shutil.rmtree(self.tmpdir)
|
454 |
|
455 |
def testShell(self): |
456 |
utils.StartDaemon("echo Hello World > %s" % self.tmpfile) |
457 |
self._wait(self.tmpfile, 60.0, "Hello World") |
458 |
|
459 |
def testShellOutput(self): |
460 |
utils.StartDaemon("echo Hello World", output=self.tmpfile) |
461 |
self._wait(self.tmpfile, 60.0, "Hello World") |
462 |
|
463 |
def testNoShellNoOutput(self): |
464 |
utils.StartDaemon(["pwd"])
|
465 |
|
466 |
def testNoShellNoOutputTouch(self): |
467 |
testfile = os.path.join(self.tmpdir, "check") |
468 |
self.failIf(os.path.exists(testfile))
|
469 |
utils.StartDaemon(["touch", testfile])
|
470 |
self._wait(testfile, 60.0, "") |
471 |
|
472 |
def testNoShellOutput(self): |
473 |
utils.StartDaemon(["pwd"], output=self.tmpfile) |
474 |
self._wait(self.tmpfile, 60.0, "/") |
475 |
|
476 |
def testNoShellOutputCwd(self): |
477 |
utils.StartDaemon(["pwd"], output=self.tmpfile, cwd=os.getcwd()) |
478 |
self._wait(self.tmpfile, 60.0, os.getcwd()) |
479 |
|
480 |
def testShellEnv(self): |
481 |
utils.StartDaemon("echo \"$GNT_TEST_VAR\"", output=self.tmpfile, |
482 |
env={ "GNT_TEST_VAR": "Hello World", }) |
483 |
self._wait(self.tmpfile, 60.0, "Hello World") |
484 |
|
485 |
def testNoShellEnv(self): |
486 |
utils.StartDaemon(["printenv", "GNT_TEST_VAR"], output=self.tmpfile, |
487 |
env={ "GNT_TEST_VAR": "Hello World", }) |
488 |
self._wait(self.tmpfile, 60.0, "Hello World") |
489 |
|
490 |
def testOutputFd(self): |
491 |
fd = os.open(self.tmpfile, os.O_WRONLY | os.O_CREAT)
|
492 |
try:
|
493 |
utils.StartDaemon(["pwd"], output_fd=fd, cwd=os.getcwd())
|
494 |
finally:
|
495 |
os.close(fd) |
496 |
self._wait(self.tmpfile, 60.0, os.getcwd()) |
497 |
|
498 |
def testPid(self): |
499 |
pid = utils.StartDaemon("echo $$ > %s" % self.tmpfile) |
500 |
self._wait(self.tmpfile, 60.0, str(pid)) |
501 |
|
502 |
def testPidFile(self): |
503 |
pidfile = os.path.join(self.tmpdir, "pid") |
504 |
checkfile = os.path.join(self.tmpdir, "abort") |
505 |
|
506 |
pid = utils.StartDaemon("while sleep 5; do :; done", pidfile=pidfile,
|
507 |
output=self.tmpfile)
|
508 |
try:
|
509 |
fd = os.open(pidfile, os.O_RDONLY) |
510 |
try:
|
511 |
# Check file is locked
|
512 |
self.assertRaises(errors.LockError, utils.LockFile, fd)
|
513 |
|
514 |
pidtext = os.read(fd, 100)
|
515 |
finally:
|
516 |
os.close(fd) |
517 |
|
518 |
self.assertEqual(int(pidtext.strip()), pid) |
519 |
|
520 |
self.assert_(utils.IsProcessAlive(pid))
|
521 |
finally:
|
522 |
# No matter what happens, kill daemon
|
523 |
utils.KillProcess(pid, timeout=5.0, waitpid=False) |
524 |
self.failIf(utils.IsProcessAlive(pid))
|
525 |
|
526 |
self.assertEqual(utils.ReadFile(self.tmpfile), "") |
527 |
|
528 |
def _wait(self, path, timeout, expected): |
529 |
# Due to the asynchronous nature of daemon processes, polling is necessary.
|
530 |
# A timeout makes sure the test doesn't hang forever.
|
531 |
def _CheckFile(): |
532 |
if not (os.path.isfile(path) and |
533 |
utils.ReadFile(path).strip() == expected): |
534 |
raise utils.RetryAgain()
|
535 |
|
536 |
try:
|
537 |
utils.Retry(_CheckFile, (0.01, 1.5, 1.0), timeout) |
538 |
except utils.RetryTimeout:
|
539 |
self.fail("Apparently the daemon didn't run in %s seconds and/or" |
540 |
" didn't write the correct output" % timeout)
|
541 |
|
542 |
def testError(self): |
543 |
self.assertRaises(errors.OpExecError, utils.StartDaemon,
|
544 |
["./does-NOT-EXIST/here/0123456789"])
|
545 |
self.assertRaises(errors.OpExecError, utils.StartDaemon,
|
546 |
["./does-NOT-EXIST/here/0123456789"],
|
547 |
output=os.path.join(self.tmpdir, "DIR/NOT/EXIST")) |
548 |
self.assertRaises(errors.OpExecError, utils.StartDaemon,
|
549 |
["./does-NOT-EXIST/here/0123456789"],
|
550 |
cwd=os.path.join(self.tmpdir, "DIR/NOT/EXIST")) |
551 |
self.assertRaises(errors.OpExecError, utils.StartDaemon,
|
552 |
["./does-NOT-EXIST/here/0123456789"],
|
553 |
output=os.path.join(self.tmpdir, "DIR/NOT/EXIST")) |
554 |
|
555 |
fd = os.open(self.tmpfile, os.O_WRONLY | os.O_CREAT)
|
556 |
try:
|
557 |
self.assertRaises(errors.ProgrammerError, utils.StartDaemon,
|
558 |
["./does-NOT-EXIST/here/0123456789"],
|
559 |
output=self.tmpfile, output_fd=fd)
|
560 |
finally:
|
561 |
os.close(fd) |
562 |
|
563 |
|
564 |
class TestParseCpuMask(unittest.TestCase): |
565 |
"""Test case for the ParseCpuMask function."""
|
566 |
|
567 |
def testWellFormed(self): |
568 |
self.assertEqual(utils.ParseCpuMask(""), []) |
569 |
self.assertEqual(utils.ParseCpuMask("1"), [1]) |
570 |
self.assertEqual(utils.ParseCpuMask("0-2,4,5-5"), [0,1,2,4,5]) |
571 |
|
572 |
def testInvalidInput(self): |
573 |
for data in ["garbage", "0,", "0-1-2", "2-1", "1-a"]: |
574 |
self.assertRaises(errors.ParseError, utils.ParseCpuMask, data)
|
575 |
|
576 |
|
577 |
class TestGetMounts(unittest.TestCase): |
578 |
"""Test case for GetMounts()."""
|
579 |
|
580 |
TESTDATA = ( |
581 |
"rootfs / rootfs rw 0 0\n"
|
582 |
"none /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\n"
|
583 |
"none /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n")
|
584 |
|
585 |
def setUp(self): |
586 |
self.tmpfile = tempfile.NamedTemporaryFile()
|
587 |
utils.WriteFile(self.tmpfile.name, data=self.TESTDATA) |
588 |
|
589 |
def testGetMounts(self): |
590 |
self.assertEqual(utils.GetMounts(filename=self.tmpfile.name), |
591 |
[ |
592 |
("rootfs", "/", "rootfs", "rw"), |
593 |
("none", "/sys", "sysfs", "rw,nosuid,nodev,noexec,relatime"), |
594 |
("none", "/proc", "proc", "rw,nosuid,nodev,noexec,relatime"), |
595 |
]) |
596 |
|
597 |
class TestNewUUID(unittest.TestCase): |
598 |
"""Test case for NewUUID"""
|
599 |
|
600 |
def runTest(self): |
601 |
self.failUnless(utils.UUID_RE.match(utils.NewUUID()))
|
602 |
|
603 |
|
604 |
class TestFirstFree(unittest.TestCase): |
605 |
"""Test case for the FirstFree function"""
|
606 |
|
607 |
def test(self): |
608 |
"""Test FirstFree"""
|
609 |
self.failUnlessEqual(FirstFree([0, 1, 3]), 2) |
610 |
self.failUnlessEqual(FirstFree([]), None) |
611 |
self.failUnlessEqual(FirstFree([3, 4, 6]), 0) |
612 |
self.failUnlessEqual(FirstFree([3, 4, 6], base=3), 5) |
613 |
self.failUnlessRaises(AssertionError, FirstFree, [0, 3, 4, 6], base=3) |
614 |
|
615 |
|
616 |
class TestTimeFunctions(unittest.TestCase): |
617 |
"""Test case for time functions"""
|
618 |
|
619 |
def runTest(self): |
620 |
self.assertEqual(utils.SplitTime(1), (1, 0)) |
621 |
self.assertEqual(utils.SplitTime(1.5), (1, 500000)) |
622 |
self.assertEqual(utils.SplitTime(1218448917.4809151), (1218448917, 480915)) |
623 |
self.assertEqual(utils.SplitTime(123.48012), (123, 480120)) |
624 |
self.assertEqual(utils.SplitTime(123.9996), (123, 999600)) |
625 |
self.assertEqual(utils.SplitTime(123.9995), (123, 999500)) |
626 |
self.assertEqual(utils.SplitTime(123.9994), (123, 999400)) |
627 |
self.assertEqual(utils.SplitTime(123.999999999), (123, 999999)) |
628 |
|
629 |
self.assertRaises(AssertionError, utils.SplitTime, -1) |
630 |
|
631 |
self.assertEqual(utils.MergeTime((1, 0)), 1.0) |
632 |
self.assertEqual(utils.MergeTime((1, 500000)), 1.5) |
633 |
self.assertEqual(utils.MergeTime((1218448917, 500000)), 1218448917.5) |
634 |
|
635 |
self.assertEqual(round(utils.MergeTime((1218448917, 481000)), 3), |
636 |
1218448917.481)
|
637 |
self.assertEqual(round(utils.MergeTime((1, 801000)), 3), 1.801) |
638 |
|
639 |
self.assertRaises(AssertionError, utils.MergeTime, (0, -1)) |
640 |
self.assertRaises(AssertionError, utils.MergeTime, (0, 1000000)) |
641 |
self.assertRaises(AssertionError, utils.MergeTime, (0, 9999999)) |
642 |
self.assertRaises(AssertionError, utils.MergeTime, (-1, 0)) |
643 |
self.assertRaises(AssertionError, utils.MergeTime, (-9999, 0)) |
644 |
|
645 |
|
646 |
class FieldSetTestCase(unittest.TestCase): |
647 |
"""Test case for FieldSets"""
|
648 |
|
649 |
def testSimpleMatch(self): |
650 |
f = utils.FieldSet("a", "b", "c", "def") |
651 |
self.failUnless(f.Matches("a")) |
652 |
self.failIf(f.Matches("d"), "Substring matched") |
653 |
self.failIf(f.Matches("defghi"), "Prefix string matched") |
654 |
self.failIf(f.NonMatching(["b", "c"])) |
655 |
self.failIf(f.NonMatching(["a", "b", "c", "def"])) |
656 |
self.failUnless(f.NonMatching(["a", "d"])) |
657 |
|
658 |
def testRegexMatch(self): |
659 |
f = utils.FieldSet("a", "b([0-9]+)", "c") |
660 |
self.failUnless(f.Matches("b1")) |
661 |
self.failUnless(f.Matches("b99")) |
662 |
self.failIf(f.Matches("b/1")) |
663 |
self.failIf(f.NonMatching(["b12", "c"])) |
664 |
self.failUnless(f.NonMatching(["a", "1"])) |
665 |
|
666 |
class TestForceDictType(unittest.TestCase): |
667 |
"""Test case for ForceDictType"""
|
668 |
KEY_TYPES = { |
669 |
"a": constants.VTYPE_INT,
|
670 |
"b": constants.VTYPE_BOOL,
|
671 |
"c": constants.VTYPE_STRING,
|
672 |
"d": constants.VTYPE_SIZE,
|
673 |
"e": constants.VTYPE_MAYBE_STRING,
|
674 |
} |
675 |
|
676 |
def _fdt(self, dict, allowed_values=None): |
677 |
if allowed_values is None: |
678 |
utils.ForceDictType(dict, self.KEY_TYPES) |
679 |
else:
|
680 |
utils.ForceDictType(dict, self.KEY_TYPES, allowed_values=allowed_values) |
681 |
|
682 |
return dict |
683 |
|
684 |
def testSimpleDict(self): |
685 |
self.assertEqual(self._fdt({}), {}) |
686 |
self.assertEqual(self._fdt({'a': 1}), {'a': 1}) |
687 |
self.assertEqual(self._fdt({'a': '1'}), {'a': 1}) |
688 |
self.assertEqual(self._fdt({'a': 1, 'b': 1}), {'a':1, 'b': True}) |
689 |
self.assertEqual(self._fdt({'b': 1, 'c': 'foo'}), {'b': True, 'c': 'foo'}) |
690 |
self.assertEqual(self._fdt({'b': 1, 'c': False}), {'b': True, 'c': ''}) |
691 |
self.assertEqual(self._fdt({'b': 'false'}), {'b': False}) |
692 |
self.assertEqual(self._fdt({'b': 'False'}), {'b': False}) |
693 |
self.assertEqual(self._fdt({'b': False}), {'b': False}) |
694 |
self.assertEqual(self._fdt({'b': 'true'}), {'b': True}) |
695 |
self.assertEqual(self._fdt({'b': 'True'}), {'b': True}) |
696 |
self.assertEqual(self._fdt({'d': '4'}), {'d': 4}) |
697 |
self.assertEqual(self._fdt({'d': '4M'}), {'d': 4}) |
698 |
self.assertEqual(self._fdt({"e": None, }), {"e": None, }) |
699 |
self.assertEqual(self._fdt({"e": "Hello World", }), {"e": "Hello World", }) |
700 |
self.assertEqual(self._fdt({"e": False, }), {"e": '', }) |
701 |
self.assertEqual(self._fdt({"b": "hello", }, ["hello"]), {"b": "hello"}) |
702 |
|
703 |
def testErrors(self): |
704 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {'a': 'astring'}) |
705 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {"b": "hello"}) |
706 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {'c': True}) |
707 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {'d': 'astring'}) |
708 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {'d': '4 L'}) |
709 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {"e": object(), }) |
710 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {"e": [], }) |
711 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, {"x": None, }) |
712 |
self.assertRaises(errors.TypeEnforcementError, self._fdt, []) |
713 |
self.assertRaises(errors.ProgrammerError, utils.ForceDictType,
|
714 |
{"b": "hello"}, {"b": "no-such-type"}) |
715 |
|
716 |
|
717 |
class RunInSeparateProcess(unittest.TestCase): |
718 |
def test(self): |
719 |
for exp in [True, False]: |
720 |
def _child(): |
721 |
return exp
|
722 |
|
723 |
self.assertEqual(exp, utils.RunInSeparateProcess(_child))
|
724 |
|
725 |
def testArgs(self): |
726 |
for arg in [0, 1, 999, "Hello World", (1, 2, 3)]: |
727 |
def _child(carg1, carg2): |
728 |
return carg1 == "Foo" and carg2 == arg |
729 |
|
730 |
self.assert_(utils.RunInSeparateProcess(_child, "Foo", arg)) |
731 |
|
732 |
def testPid(self): |
733 |
parent_pid = os.getpid() |
734 |
|
735 |
def _check(): |
736 |
return os.getpid() == parent_pid
|
737 |
|
738 |
self.failIf(utils.RunInSeparateProcess(_check))
|
739 |
|
740 |
def testSignal(self): |
741 |
def _kill(): |
742 |
os.kill(os.getpid(), signal.SIGTERM) |
743 |
|
744 |
self.assertRaises(errors.GenericError,
|
745 |
utils.RunInSeparateProcess, _kill) |
746 |
|
747 |
def testException(self): |
748 |
def _exc(): |
749 |
raise errors.GenericError("This is a test") |
750 |
|
751 |
self.assertRaises(errors.GenericError,
|
752 |
utils.RunInSeparateProcess, _exc) |
753 |
|
754 |
|
755 |
class TestValidateServiceName(unittest.TestCase): |
756 |
def testValid(self): |
757 |
testnames = [ |
758 |
0, 1, 2, 3, 1024, 65000, 65534, 65535, |
759 |
"ganeti",
|
760 |
"gnt-masterd",
|
761 |
"HELLO_WORLD_SVC",
|
762 |
"hello.world.1",
|
763 |
"0", "80", "1111", "65535", |
764 |
] |
765 |
|
766 |
for name in testnames: |
767 |
self.assertEqual(utils.ValidateServiceName(name), name)
|
768 |
|
769 |
def testInvalid(self): |
770 |
testnames = [ |
771 |
-15756, -1, 65536, 133428083, |
772 |
"", "Hello World!", "!", "'", "\"", "\t", "\n", "`", |
773 |
"-8546", "-1", "65536", |
774 |
(129 * "A"), |
775 |
] |
776 |
|
777 |
for name in testnames: |
778 |
self.assertRaises(errors.OpPrereqError, utils.ValidateServiceName, name)
|
779 |
|
780 |
|
781 |
class TestReadLockedPidFile(unittest.TestCase): |
782 |
def setUp(self): |
783 |
self.tmpdir = tempfile.mkdtemp()
|
784 |
|
785 |
def tearDown(self): |
786 |
shutil.rmtree(self.tmpdir)
|
787 |
|
788 |
def testNonExistent(self): |
789 |
path = utils.PathJoin(self.tmpdir, "nonexist") |
790 |
self.assert_(utils.ReadLockedPidFile(path) is None) |
791 |
|
792 |
def testUnlocked(self): |
793 |
path = utils.PathJoin(self.tmpdir, "pid") |
794 |
utils.WriteFile(path, data="123")
|
795 |
self.assert_(utils.ReadLockedPidFile(path) is None) |
796 |
|
797 |
def testLocked(self): |
798 |
path = utils.PathJoin(self.tmpdir, "pid") |
799 |
utils.WriteFile(path, data="123")
|
800 |
|
801 |
fl = utils.FileLock.Open(path) |
802 |
try:
|
803 |
fl.Exclusive(blocking=True)
|
804 |
|
805 |
self.assertEqual(utils.ReadLockedPidFile(path), 123) |
806 |
finally:
|
807 |
fl.Close() |
808 |
|
809 |
self.assert_(utils.ReadLockedPidFile(path) is None) |
810 |
|
811 |
def testError(self): |
812 |
path = utils.PathJoin(self.tmpdir, "foobar", "pid") |
813 |
utils.WriteFile(utils.PathJoin(self.tmpdir, "foobar"), data="") |
814 |
# open(2) should return ENOTDIR
|
815 |
self.assertRaises(EnvironmentError, utils.ReadLockedPidFile, path) |
816 |
|
817 |
|
818 |
class TestFindMatch(unittest.TestCase): |
819 |
def test(self): |
820 |
data = { |
821 |
"aaaa": "Four A", |
822 |
"bb": {"Two B": True}, |
823 |
re.compile(r"^x(foo|bar|bazX)([0-9]+)$"): (1, 2, 3), |
824 |
} |
825 |
|
826 |
self.assertEqual(utils.FindMatch(data, "aaaa"), ("Four A", [])) |
827 |
self.assertEqual(utils.FindMatch(data, "bb"), ({"Two B": True}, [])) |
828 |
|
829 |
for i in ["foo", "bar", "bazX"]: |
830 |
for j in range(1, 100, 7): |
831 |
self.assertEqual(utils.FindMatch(data, "x%s%s" % (i, j)), |
832 |
((1, 2, 3), [i, str(j)])) |
833 |
|
834 |
def testNoMatch(self): |
835 |
self.assert_(utils.FindMatch({}, "") is None) |
836 |
self.assert_(utils.FindMatch({}, "foo") is None) |
837 |
self.assert_(utils.FindMatch({}, 1234) is None) |
838 |
|
839 |
data = { |
840 |
"X": "Hello World", |
841 |
re.compile("^(something)$"): "Hello World", |
842 |
} |
843 |
|
844 |
self.assert_(utils.FindMatch(data, "") is None) |
845 |
self.assert_(utils.FindMatch(data, "Hello World") is None) |
846 |
|
847 |
|
848 |
class TimeMock: |
849 |
def __init__(self, values): |
850 |
self.values = values
|
851 |
|
852 |
def __call__(self): |
853 |
return self.values.pop(0) |
854 |
|
855 |
|
856 |
class TestRunningTimeout(unittest.TestCase): |
857 |
def setUp(self): |
858 |
self.time_fn = TimeMock([0.0, 0.3, 4.6, 6.5]) |
859 |
|
860 |
def testRemainingFloat(self): |
861 |
timeout = utils.RunningTimeout(5.0, True, _time_fn=self.time_fn) |
862 |
self.assertAlmostEqual(timeout.Remaining(), 4.7) |
863 |
self.assertAlmostEqual(timeout.Remaining(), 0.4) |
864 |
self.assertAlmostEqual(timeout.Remaining(), -1.5) |
865 |
|
866 |
def testRemaining(self): |
867 |
self.time_fn = TimeMock([0, 2, 4, 5, 6]) |
868 |
timeout = utils.RunningTimeout(5, True, _time_fn=self.time_fn) |
869 |
self.assertEqual(timeout.Remaining(), 3) |
870 |
self.assertEqual(timeout.Remaining(), 1) |
871 |
self.assertEqual(timeout.Remaining(), 0) |
872 |
self.assertEqual(timeout.Remaining(), -1) |
873 |
|
874 |
def testRemainingNonNegative(self): |
875 |
timeout = utils.RunningTimeout(5.0, False, _time_fn=self.time_fn) |
876 |
self.assertAlmostEqual(timeout.Remaining(), 4.7) |
877 |
self.assertAlmostEqual(timeout.Remaining(), 0.4) |
878 |
self.assertEqual(timeout.Remaining(), 0.0) |
879 |
|
880 |
def testNegativeTimeout(self): |
881 |
self.assertRaises(ValueError, utils.RunningTimeout, -1.0, True) |
882 |
|
883 |
|
884 |
class TestTryConvert(unittest.TestCase): |
885 |
def test(self): |
886 |
for src, fn, result in [ |
887 |
("1", int, 1), |
888 |
("a", int, "a"), |
889 |
("", bool, False), |
890 |
("a", bool, True), |
891 |
]: |
892 |
self.assertEqual(utils.TryConvert(fn, src), result)
|
893 |
|
894 |
|
895 |
class TestIsValidShellParam(unittest.TestCase): |
896 |
def test(self): |
897 |
for val, result in [ |
898 |
("abc", True), |
899 |
("ab;cd", False), |
900 |
]: |
901 |
self.assertEqual(utils.IsValidShellParam(val), result)
|
902 |
|
903 |
|
904 |
class TestBuildShellCmd(unittest.TestCase): |
905 |
def test(self): |
906 |
self.assertRaises(errors.ProgrammerError, utils.BuildShellCmd,
|
907 |
"ls %s", "ab;cd") |
908 |
self.assertEqual(utils.BuildShellCmd("ls %s", "ab"), "ls ab") |
909 |
|
910 |
|
911 |
if __name__ == '__main__': |
912 |
testutils.GanetiTestProgram() |