root / test / ganeti.cli_unittest.py @ f2c6673d
History | View | Annotate | Download (26.3 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2008, 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 cli module"""
|
23 |
|
24 |
import unittest |
25 |
from cStringIO import StringIO |
26 |
|
27 |
import ganeti |
28 |
import testutils |
29 |
|
30 |
from ganeti import constants |
31 |
from ganeti import cli |
32 |
from ganeti import errors |
33 |
from ganeti import utils |
34 |
from ganeti import objects |
35 |
from ganeti.errors import OpPrereqError, ParameterError |
36 |
|
37 |
|
38 |
class TestParseTimespec(unittest.TestCase): |
39 |
"""Testing case for ParseTimespec"""
|
40 |
|
41 |
def testValidTimes(self): |
42 |
"""Test valid timespecs"""
|
43 |
test_data = [ |
44 |
('1s', 1), |
45 |
('1', 1), |
46 |
('1m', 60), |
47 |
('1h', 60 * 60), |
48 |
('1d', 60 * 60 * 24), |
49 |
('1w', 60 * 60 * 24 * 7), |
50 |
('4h', 4 * 60 * 60), |
51 |
('61m', 61 * 60), |
52 |
] |
53 |
for value, expected_result in test_data: |
54 |
self.failUnlessEqual(cli.ParseTimespec(value), expected_result)
|
55 |
|
56 |
def testInvalidTime(self): |
57 |
"""Test invalid timespecs"""
|
58 |
test_data = [ |
59 |
'1y',
|
60 |
'',
|
61 |
'aaa',
|
62 |
's',
|
63 |
] |
64 |
for value in test_data: |
65 |
self.failUnlessRaises(OpPrereqError, cli.ParseTimespec, value)
|
66 |
|
67 |
|
68 |
class TestSplitKeyVal(unittest.TestCase): |
69 |
"""Testing case for cli._SplitKeyVal"""
|
70 |
DATA = "a=b,c,no_d,-e"
|
71 |
RESULT = {"a": "b", "c": True, "d": False, "e": None} |
72 |
|
73 |
def testSplitKeyVal(self): |
74 |
"""Test splitting"""
|
75 |
self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA), self.RESULT) |
76 |
|
77 |
def testDuplicateParam(self): |
78 |
"""Test duplicate parameters"""
|
79 |
for data in ("a=1,a=2", "a,no_a"): |
80 |
self.failUnlessRaises(ParameterError, cli._SplitKeyVal,
|
81 |
"option", data)
|
82 |
|
83 |
def testEmptyData(self): |
84 |
"""Test how we handle splitting an empty string"""
|
85 |
self.failUnlessEqual(cli._SplitKeyVal("option", ""), {}) |
86 |
|
87 |
class TestIdentKeyVal(unittest.TestCase): |
88 |
"""Testing case for cli.check_ident_key_val"""
|
89 |
|
90 |
def testIdentKeyVal(self): |
91 |
"""Test identkeyval"""
|
92 |
def cikv(value): |
93 |
return cli.check_ident_key_val("option", "opt", value) |
94 |
|
95 |
self.assertEqual(cikv("foo:bar"), ("foo", {"bar": True})) |
96 |
self.assertEqual(cikv("foo:bar=baz"), ("foo", {"bar": "baz"})) |
97 |
self.assertEqual(cikv("bar:b=c,c=a"), ("bar", {"b": "c", "c": "a"})) |
98 |
self.assertEqual(cikv("no_bar"), ("bar", False)) |
99 |
self.assertRaises(ParameterError, cikv, "no_bar:foo") |
100 |
self.assertRaises(ParameterError, cikv, "no_bar:foo=baz") |
101 |
self.assertEqual(cikv("-foo"), ("foo", None)) |
102 |
self.assertRaises(ParameterError, cikv, "-foo:a=c") |
103 |
|
104 |
|
105 |
class TestToStream(unittest.TestCase): |
106 |
"""Test the ToStream functions"""
|
107 |
|
108 |
def testBasic(self): |
109 |
for data in ["foo", |
110 |
"foo %s",
|
111 |
"foo %(test)s",
|
112 |
"foo %s %s",
|
113 |
"",
|
114 |
]: |
115 |
buf = StringIO() |
116 |
cli._ToStream(buf, data) |
117 |
self.failUnlessEqual(buf.getvalue(), data+'\n') |
118 |
|
119 |
def testParams(self): |
120 |
buf = StringIO() |
121 |
cli._ToStream(buf, "foo %s", 1) |
122 |
self.failUnlessEqual(buf.getvalue(), "foo 1\n") |
123 |
buf = StringIO() |
124 |
cli._ToStream(buf, "foo %s", (15,16)) |
125 |
self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n") |
126 |
buf = StringIO() |
127 |
cli._ToStream(buf, "foo %s %s", "a", "b") |
128 |
self.failUnlessEqual(buf.getvalue(), "foo a b\n") |
129 |
|
130 |
|
131 |
class TestGenerateTable(unittest.TestCase): |
132 |
HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)]) |
133 |
|
134 |
FIELDS1 = ["f1", "f2"] |
135 |
DATA1 = [ |
136 |
["abc", 1234], |
137 |
["foobar", 56], |
138 |
["b", -14], |
139 |
] |
140 |
|
141 |
def _test(self, headers, fields, separator, data, |
142 |
numfields, unitfields, units, expected): |
143 |
table = cli.GenerateTable(headers, fields, separator, data, |
144 |
numfields=numfields, unitfields=unitfields, |
145 |
units=units) |
146 |
self.assertEqual(table, expected)
|
147 |
|
148 |
def testPlain(self): |
149 |
exp = [ |
150 |
"Field1 Field2",
|
151 |
"abc 1234",
|
152 |
"foobar 56",
|
153 |
"b -14",
|
154 |
] |
155 |
self._test(self.HEADERS, self.FIELDS1, None, self.DATA1, |
156 |
None, None, "m", exp) |
157 |
|
158 |
def testNoFields(self): |
159 |
self._test(self.HEADERS, [], None, [[], []], |
160 |
None, None, "m", ["", "", ""]) |
161 |
self._test(None, [], None, [[], []], |
162 |
None, None, "m", ["", ""]) |
163 |
|
164 |
def testSeparator(self): |
165 |
for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]: |
166 |
exp = [ |
167 |
"Field1%sField2" % sep,
|
168 |
"abc%s1234" % sep,
|
169 |
"foobar%s56" % sep,
|
170 |
"b%s-14" % sep,
|
171 |
] |
172 |
self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1, |
173 |
None, None, "m", exp) |
174 |
|
175 |
def testNoHeader(self): |
176 |
exp = [ |
177 |
"abc 1234",
|
178 |
"foobar 56",
|
179 |
"b -14",
|
180 |
] |
181 |
self._test(None, self.FIELDS1, None, self.DATA1, |
182 |
None, None, "m", exp) |
183 |
|
184 |
def testUnknownField(self): |
185 |
headers = { |
186 |
"f1": "Field1", |
187 |
} |
188 |
exp = [ |
189 |
"Field1 UNKNOWN",
|
190 |
"abc 1234",
|
191 |
"foobar 56",
|
192 |
"b -14",
|
193 |
] |
194 |
self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1, |
195 |
None, None, "m", exp) |
196 |
|
197 |
def testNumfields(self): |
198 |
fields = ["f1", "f2", "f3"] |
199 |
data = [ |
200 |
["abc", 1234, 0], |
201 |
["foobar", 56, 3], |
202 |
["b", -14, "-"], |
203 |
] |
204 |
exp = [ |
205 |
"Field1 Field2 Field3",
|
206 |
"abc 1234 0",
|
207 |
"foobar 56 3",
|
208 |
"b -14 -",
|
209 |
] |
210 |
self._test(self.HEADERS, fields, None, data, |
211 |
["f2", "f3"], None, "m", exp) |
212 |
|
213 |
def testUnitfields(self): |
214 |
expnosep = [ |
215 |
"Field1 Field2 Field3",
|
216 |
"abc 1234 0M",
|
217 |
"foobar 56 3M",
|
218 |
"b -14 -",
|
219 |
] |
220 |
|
221 |
expsep = [ |
222 |
"Field1:Field2:Field3",
|
223 |
"abc:1234:0M",
|
224 |
"foobar:56:3M",
|
225 |
"b:-14:-",
|
226 |
] |
227 |
|
228 |
for sep, expected in [(None, expnosep), (":", expsep)]: |
229 |
fields = ["f1", "f2", "f3"] |
230 |
data = [ |
231 |
["abc", 1234, 0], |
232 |
["foobar", 56, 3], |
233 |
["b", -14, "-"], |
234 |
] |
235 |
self._test(self.HEADERS, fields, sep, data, |
236 |
["f2", "f3"], ["f3"], "h", expected) |
237 |
|
238 |
def testUnusual(self): |
239 |
data = [ |
240 |
["%", "xyz"], |
241 |
["%%", "abc"], |
242 |
] |
243 |
exp = [ |
244 |
"Field1 Field2",
|
245 |
"% xyz",
|
246 |
"%% abc",
|
247 |
] |
248 |
self._test(self.HEADERS, ["f1", "f2"], None, data, |
249 |
None, None, "m", exp) |
250 |
|
251 |
|
252 |
class TestFormatQueryResult(unittest.TestCase): |
253 |
def test(self): |
254 |
fields = [ |
255 |
objects.QueryFieldDefinition(name="name", title="Name", |
256 |
kind=constants.QFT_TEXT), |
257 |
objects.QueryFieldDefinition(name="size", title="Size", |
258 |
kind=constants.QFT_NUMBER), |
259 |
objects.QueryFieldDefinition(name="act", title="Active", |
260 |
kind=constants.QFT_BOOL), |
261 |
objects.QueryFieldDefinition(name="mem", title="Memory", |
262 |
kind=constants.QFT_UNIT), |
263 |
objects.QueryFieldDefinition(name="other", title="SomeList", |
264 |
kind=constants.QFT_OTHER), |
265 |
] |
266 |
|
267 |
response = objects.QueryResponse(fields=fields, data=[ |
268 |
[(constants.RS_NORMAL, "nodeA"), (constants.RS_NORMAL, 128), |
269 |
(constants.RS_NORMAL, False), (constants.RS_NORMAL, 1468006), |
270 |
(constants.RS_NORMAL, [])], |
271 |
[(constants.RS_NORMAL, "other"), (constants.RS_NORMAL, 512), |
272 |
(constants.RS_NORMAL, True), (constants.RS_NORMAL, 16), |
273 |
(constants.RS_NORMAL, [1, 2, 3])], |
274 |
[(constants.RS_NORMAL, "xyz"), (constants.RS_NORMAL, 1024), |
275 |
(constants.RS_NORMAL, True), (constants.RS_NORMAL, 4096), |
276 |
(constants.RS_NORMAL, [{}, {}])], |
277 |
]) |
278 |
|
279 |
self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True), |
280 |
(cli.QR_NORMAL, [ |
281 |
"Name Size Active Memory SomeList",
|
282 |
"nodeA 128 N 1.4T []",
|
283 |
"other 512 Y 16M [1, 2, 3]",
|
284 |
"xyz 1024 Y 4.0G [{}, {}]",
|
285 |
])) |
286 |
|
287 |
def testTimestampAndUnit(self): |
288 |
fields = [ |
289 |
objects.QueryFieldDefinition(name="name", title="Name", |
290 |
kind=constants.QFT_TEXT), |
291 |
objects.QueryFieldDefinition(name="size", title="Size", |
292 |
kind=constants.QFT_UNIT), |
293 |
objects.QueryFieldDefinition(name="mtime", title="ModTime", |
294 |
kind=constants.QFT_TIMESTAMP), |
295 |
] |
296 |
|
297 |
response = objects.QueryResponse(fields=fields, data=[ |
298 |
[(constants.RS_NORMAL, "a"), (constants.RS_NORMAL, 1024), |
299 |
(constants.RS_NORMAL, 0)],
|
300 |
[(constants.RS_NORMAL, "b"), (constants.RS_NORMAL, 144996), |
301 |
(constants.RS_NORMAL, 1291746295)],
|
302 |
]) |
303 |
|
304 |
self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True), |
305 |
(cli.QR_NORMAL, [ |
306 |
"Name Size ModTime",
|
307 |
"a 1024 %s" % utils.FormatTime(0), |
308 |
"b 144996 %s" % utils.FormatTime(1291746295), |
309 |
])) |
310 |
|
311 |
def testOverride(self): |
312 |
fields = [ |
313 |
objects.QueryFieldDefinition(name="name", title="Name", |
314 |
kind=constants.QFT_TEXT), |
315 |
objects.QueryFieldDefinition(name="cust", title="Custom", |
316 |
kind=constants.QFT_OTHER), |
317 |
objects.QueryFieldDefinition(name="xt", title="XTime", |
318 |
kind=constants.QFT_TIMESTAMP), |
319 |
] |
320 |
|
321 |
response = objects.QueryResponse(fields=fields, data=[ |
322 |
[(constants.RS_NORMAL, "x"), (constants.RS_NORMAL, ["a", "b", "c"]), |
323 |
(constants.RS_NORMAL, 1234)],
|
324 |
[(constants.RS_NORMAL, "y"), (constants.RS_NORMAL, range(10)), |
325 |
(constants.RS_NORMAL, 1291746295)],
|
326 |
]) |
327 |
|
328 |
override = { |
329 |
"cust": (utils.CommaJoin, False), |
330 |
"xt": (hex, True), |
331 |
} |
332 |
|
333 |
self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True, |
334 |
format_override=override), |
335 |
(cli.QR_NORMAL, [ |
336 |
"Name Custom XTime",
|
337 |
"x a, b, c 0x4d2",
|
338 |
"y 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
|
339 |
])) |
340 |
|
341 |
def testSeparator(self): |
342 |
fields = [ |
343 |
objects.QueryFieldDefinition(name="name", title="Name", |
344 |
kind=constants.QFT_TEXT), |
345 |
objects.QueryFieldDefinition(name="count", title="Count", |
346 |
kind=constants.QFT_NUMBER), |
347 |
objects.QueryFieldDefinition(name="desc", title="Description", |
348 |
kind=constants.QFT_TEXT), |
349 |
] |
350 |
|
351 |
response = objects.QueryResponse(fields=fields, data=[ |
352 |
[(constants.RS_NORMAL, "instance1.example.com"),
|
353 |
(constants.RS_NORMAL, 21125), (constants.RS_NORMAL, "Hello World!")], |
354 |
[(constants.RS_NORMAL, "mail.other.net"),
|
355 |
(constants.RS_NORMAL, -9000), (constants.RS_NORMAL, "a,b,c")], |
356 |
]) |
357 |
|
358 |
for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]: |
359 |
for header in [None, "Name%sCount%sDescription" % (sep, sep)]: |
360 |
exp = [] |
361 |
if header:
|
362 |
exp.append(header) |
363 |
exp.extend([ |
364 |
"instance1.example.com%s21125%sHello World!" % (sep, sep),
|
365 |
"mail.other.net%s-9000%sa,b,c" % (sep, sep),
|
366 |
]) |
367 |
|
368 |
self.assertEqual(cli.FormatQueryResult(response, separator=sep,
|
369 |
header=bool(header)),
|
370 |
(cli.QR_NORMAL, exp)) |
371 |
|
372 |
def testStatusWithUnknown(self): |
373 |
fields = [ |
374 |
objects.QueryFieldDefinition(name="id", title="ID", |
375 |
kind=constants.QFT_NUMBER), |
376 |
objects.QueryFieldDefinition(name="unk", title="unk", |
377 |
kind=constants.QFT_UNKNOWN), |
378 |
objects.QueryFieldDefinition(name="unavail", title="Unavail", |
379 |
kind=constants.QFT_BOOL), |
380 |
objects.QueryFieldDefinition(name="nodata", title="NoData", |
381 |
kind=constants.QFT_TEXT), |
382 |
objects.QueryFieldDefinition(name="offline", title="OffLine", |
383 |
kind=constants.QFT_TEXT), |
384 |
] |
385 |
|
386 |
response = objects.QueryResponse(fields=fields, data=[ |
387 |
[(constants.RS_NORMAL, 1), (constants.RS_UNKNOWN, None), |
388 |
(constants.RS_NORMAL, False), (constants.RS_NORMAL, ""), |
389 |
(constants.RS_OFFLINE, None)],
|
390 |
[(constants.RS_NORMAL, 2), (constants.RS_UNKNOWN, None), |
391 |
(constants.RS_NODATA, None), (constants.RS_NORMAL, "x"), |
392 |
(constants.RS_OFFLINE, None)],
|
393 |
[(constants.RS_NORMAL, 3), (constants.RS_UNKNOWN, None), |
394 |
(constants.RS_NORMAL, False), (constants.RS_UNAVAIL, None), |
395 |
(constants.RS_OFFLINE, None)],
|
396 |
]) |
397 |
|
398 |
self.assertEqual(cli.FormatQueryResult(response, header=True, |
399 |
separator="|", verbose=True), |
400 |
(cli.QR_UNKNOWN, [ |
401 |
"ID|unk|Unavail|NoData|OffLine",
|
402 |
"1|(unknown)|N||(offline)",
|
403 |
"2|(unknown)|(nodata)|x|(offline)",
|
404 |
"3|(unknown)|N|(unavail)|(offline)",
|
405 |
])) |
406 |
self.assertEqual(cli.FormatQueryResult(response, header=True, |
407 |
separator="|", verbose=False), |
408 |
(cli.QR_UNKNOWN, [ |
409 |
"ID|unk|Unavail|NoData|OffLine",
|
410 |
"1|??|N||*",
|
411 |
"2|??|?|x|*",
|
412 |
"3|??|N|-|*",
|
413 |
])) |
414 |
|
415 |
def testNoData(self): |
416 |
fields = [ |
417 |
objects.QueryFieldDefinition(name="id", title="ID", |
418 |
kind=constants.QFT_NUMBER), |
419 |
objects.QueryFieldDefinition(name="name", title="Name", |
420 |
kind=constants.QFT_TEXT), |
421 |
] |
422 |
|
423 |
response = objects.QueryResponse(fields=fields, data=[]) |
424 |
|
425 |
self.assertEqual(cli.FormatQueryResult(response, header=True), |
426 |
(cli.QR_NORMAL, ["ID Name"]))
|
427 |
|
428 |
def testNoDataWithUnknown(self): |
429 |
fields = [ |
430 |
objects.QueryFieldDefinition(name="id", title="ID", |
431 |
kind=constants.QFT_NUMBER), |
432 |
objects.QueryFieldDefinition(name="unk", title="unk", |
433 |
kind=constants.QFT_UNKNOWN), |
434 |
] |
435 |
|
436 |
response = objects.QueryResponse(fields=fields, data=[]) |
437 |
|
438 |
self.assertEqual(cli.FormatQueryResult(response, header=False), |
439 |
(cli.QR_UNKNOWN, [])) |
440 |
|
441 |
def testStatus(self): |
442 |
fields = [ |
443 |
objects.QueryFieldDefinition(name="id", title="ID", |
444 |
kind=constants.QFT_NUMBER), |
445 |
objects.QueryFieldDefinition(name="unavail", title="Unavail", |
446 |
kind=constants.QFT_BOOL), |
447 |
objects.QueryFieldDefinition(name="nodata", title="NoData", |
448 |
kind=constants.QFT_TEXT), |
449 |
objects.QueryFieldDefinition(name="offline", title="OffLine", |
450 |
kind=constants.QFT_TEXT), |
451 |
] |
452 |
|
453 |
response = objects.QueryResponse(fields=fields, data=[ |
454 |
[(constants.RS_NORMAL, 1), (constants.RS_NORMAL, False), |
455 |
(constants.RS_NORMAL, ""), (constants.RS_OFFLINE, None)], |
456 |
[(constants.RS_NORMAL, 2), (constants.RS_NODATA, None), |
457 |
(constants.RS_NORMAL, "x"), (constants.RS_NORMAL, "abc")], |
458 |
[(constants.RS_NORMAL, 3), (constants.RS_NORMAL, False), |
459 |
(constants.RS_UNAVAIL, None), (constants.RS_OFFLINE, None)], |
460 |
]) |
461 |
|
462 |
self.assertEqual(cli.FormatQueryResult(response, header=False, |
463 |
separator="|", verbose=True), |
464 |
(cli.QR_INCOMPLETE, [ |
465 |
"1|N||(offline)",
|
466 |
"2|(nodata)|x|abc",
|
467 |
"3|N|(unavail)|(offline)",
|
468 |
])) |
469 |
self.assertEqual(cli.FormatQueryResult(response, header=False, |
470 |
separator="|", verbose=False), |
471 |
(cli.QR_INCOMPLETE, [ |
472 |
"1|N||*",
|
473 |
"2|?|x|abc",
|
474 |
"3|N|-|*",
|
475 |
])) |
476 |
|
477 |
def testInvalidFieldType(self): |
478 |
fields = [ |
479 |
objects.QueryFieldDefinition(name="x", title="x", |
480 |
kind="#some#other#type"),
|
481 |
] |
482 |
|
483 |
response = objects.QueryResponse(fields=fields, data=[]) |
484 |
|
485 |
self.assertRaises(NotImplementedError, cli.FormatQueryResult, response) |
486 |
|
487 |
def testInvalidFieldStatus(self): |
488 |
fields = [ |
489 |
objects.QueryFieldDefinition(name="x", title="x", |
490 |
kind=constants.QFT_TEXT), |
491 |
] |
492 |
|
493 |
response = objects.QueryResponse(fields=fields, data=[[(-1, None)]]) |
494 |
self.assertRaises(NotImplementedError, cli.FormatQueryResult, response) |
495 |
|
496 |
response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]]) |
497 |
self.assertRaises(AssertionError, cli.FormatQueryResult, response) |
498 |
|
499 |
def testEmptyFieldTitle(self): |
500 |
fields = [ |
501 |
objects.QueryFieldDefinition(name="x", title="", |
502 |
kind=constants.QFT_TEXT), |
503 |
] |
504 |
|
505 |
response = objects.QueryResponse(fields=fields, data=[]) |
506 |
self.assertRaises(AssertionError, cli.FormatQueryResult, response) |
507 |
|
508 |
|
509 |
class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase): |
510 |
def __init__(self, tc, job_id): |
511 |
self.tc = tc
|
512 |
self.job_id = job_id
|
513 |
self._wfjcr = []
|
514 |
self._jobstatus = []
|
515 |
self._expect_notchanged = False |
516 |
self._expect_log = []
|
517 |
|
518 |
def CheckEmpty(self): |
519 |
self.tc.assertFalse(self._wfjcr) |
520 |
self.tc.assertFalse(self._jobstatus) |
521 |
self.tc.assertFalse(self._expect_notchanged) |
522 |
self.tc.assertFalse(self._expect_log) |
523 |
|
524 |
def AddWfjcResult(self, *args): |
525 |
self._wfjcr.append(args)
|
526 |
|
527 |
def AddQueryJobsResult(self, *args): |
528 |
self._jobstatus.append(args)
|
529 |
|
530 |
def WaitForJobChangeOnce(self, job_id, fields, |
531 |
prev_job_info, prev_log_serial): |
532 |
self.tc.assertEqual(job_id, self.job_id) |
533 |
self.tc.assertEqualValues(fields, ["status"]) |
534 |
self.tc.assertFalse(self._expect_notchanged) |
535 |
self.tc.assertFalse(self._expect_log) |
536 |
|
537 |
(exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0) |
538 |
self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
|
539 |
self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
|
540 |
|
541 |
if result == constants.JOB_NOTCHANGED:
|
542 |
self._expect_notchanged = True |
543 |
elif result:
|
544 |
(_, logmsgs) = result |
545 |
if logmsgs:
|
546 |
self._expect_log.extend(logmsgs)
|
547 |
|
548 |
return result
|
549 |
|
550 |
def QueryJobs(self, job_ids, fields): |
551 |
self.tc.assertEqual(job_ids, [self.job_id]) |
552 |
self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"]) |
553 |
self.tc.assertFalse(self._expect_notchanged) |
554 |
self.tc.assertFalse(self._expect_log) |
555 |
|
556 |
result = self._jobstatus.pop(0) |
557 |
self.tc.assertEqual(len(fields), len(result)) |
558 |
return [result]
|
559 |
|
560 |
def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg): |
561 |
self.tc.assertEqual(job_id, self.job_id) |
562 |
self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
|
563 |
self._expect_log.pop(0)) |
564 |
|
565 |
def ReportNotChanged(self, job_id, status): |
566 |
self.tc.assertEqual(job_id, self.job_id) |
567 |
self.tc.assert_(self._expect_notchanged) |
568 |
self._expect_notchanged = False |
569 |
|
570 |
|
571 |
class TestGenericPollJob(testutils.GanetiTestCase): |
572 |
def testSuccessWithLog(self): |
573 |
job_id = 29609
|
574 |
cbs = _MockJobPollCb(self, job_id)
|
575 |
|
576 |
cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED) |
577 |
|
578 |
cbs.AddWfjcResult(None, None, |
579 |
((constants.JOB_STATUS_QUEUED, ), None))
|
580 |
|
581 |
cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
|
582 |
constants.JOB_NOTCHANGED) |
583 |
|
584 |
cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
|
585 |
((constants.JOB_STATUS_RUNNING, ), |
586 |
[(1, utils.SplitTime(1273491611.0), |
587 |
constants.ELOG_MESSAGE, "Step 1"),
|
588 |
(2, utils.SplitTime(1273491615.9), |
589 |
constants.ELOG_MESSAGE, "Step 2"),
|
590 |
(3, utils.SplitTime(1273491625.02), |
591 |
constants.ELOG_MESSAGE, "Step 3"),
|
592 |
(4, utils.SplitTime(1273491635.05), |
593 |
constants.ELOG_MESSAGE, "Step 4"),
|
594 |
(37, utils.SplitTime(1273491645.0), |
595 |
constants.ELOG_MESSAGE, "Step 5"),
|
596 |
(203, utils.SplitTime(127349155.0), |
597 |
constants.ELOG_MESSAGE, "Step 6")]))
|
598 |
|
599 |
cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
|
600 |
((constants.JOB_STATUS_RUNNING, ), |
601 |
[(300, utils.SplitTime(1273491711.01), |
602 |
constants.ELOG_MESSAGE, "Step X"),
|
603 |
(302, utils.SplitTime(1273491815.8), |
604 |
constants.ELOG_MESSAGE, "Step Y"),
|
605 |
(303, utils.SplitTime(1273491925.32), |
606 |
constants.ELOG_MESSAGE, "Step Z")]))
|
607 |
|
608 |
cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
|
609 |
((constants.JOB_STATUS_SUCCESS, ), None))
|
610 |
|
611 |
cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS, |
612 |
[constants.OP_STATUS_SUCCESS, |
613 |
constants.OP_STATUS_SUCCESS], |
614 |
["Hello World", "Foo man bar"]) |
615 |
|
616 |
self.assertEqual(["Hello World", "Foo man bar"], |
617 |
cli.GenericPollJob(job_id, cbs, cbs)) |
618 |
cbs.CheckEmpty() |
619 |
|
620 |
def testJobLost(self): |
621 |
job_id = 13746
|
622 |
|
623 |
cbs = _MockJobPollCb(self, job_id)
|
624 |
cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED) |
625 |
cbs.AddWfjcResult(None, None, None) |
626 |
self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
|
627 |
cbs.CheckEmpty() |
628 |
|
629 |
def testError(self): |
630 |
job_id = 31088
|
631 |
|
632 |
cbs = _MockJobPollCb(self, job_id)
|
633 |
cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED) |
634 |
cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None)) |
635 |
cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR, |
636 |
[constants.OP_STATUS_SUCCESS, |
637 |
constants.OP_STATUS_ERROR], |
638 |
["Hello World", "Error code 123"]) |
639 |
self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
|
640 |
cbs.CheckEmpty() |
641 |
|
642 |
def testError2(self): |
643 |
job_id = 22235
|
644 |
|
645 |
cbs = _MockJobPollCb(self, job_id)
|
646 |
cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None)) |
647 |
encexc = errors.EncodeException(errors.LockError("problem"))
|
648 |
cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR, |
649 |
[constants.OP_STATUS_ERROR], [encexc]) |
650 |
self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
|
651 |
cbs.CheckEmpty() |
652 |
|
653 |
def testWeirdError(self): |
654 |
job_id = 28847
|
655 |
|
656 |
cbs = _MockJobPollCb(self, job_id)
|
657 |
cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None)) |
658 |
cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR, |
659 |
[constants.OP_STATUS_RUNNING, |
660 |
constants.OP_STATUS_RUNNING], |
661 |
[None, None]) |
662 |
self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
|
663 |
cbs.CheckEmpty() |
664 |
|
665 |
def testCancel(self): |
666 |
job_id = 4275
|
667 |
|
668 |
cbs = _MockJobPollCb(self, job_id)
|
669 |
cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED) |
670 |
cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None)) |
671 |
cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING, |
672 |
[constants.OP_STATUS_CANCELING, |
673 |
constants.OP_STATUS_CANCELING], |
674 |
[None, None]) |
675 |
self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
|
676 |
cbs.CheckEmpty() |
677 |
|
678 |
|
679 |
class TestFormatLogMessage(unittest.TestCase): |
680 |
def test(self): |
681 |
self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
|
682 |
"Hello World"),
|
683 |
"Hello World")
|
684 |
self.assertRaises(TypeError, cli.FormatLogMessage, |
685 |
constants.ELOG_MESSAGE, [1, 2, 3]) |
686 |
|
687 |
self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3))) |
688 |
|
689 |
|
690 |
class TestParseFields(unittest.TestCase): |
691 |
def test(self): |
692 |
self.assertEqual(cli.ParseFields(None, []), []) |
693 |
self.assertEqual(cli.ParseFields("name,foo,hello", []), |
694 |
["name", "foo", "hello"]) |
695 |
self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]), |
696 |
["def", "ault", "fields", "here"]) |
697 |
self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]), |
698 |
["name", "foo"]) |
699 |
self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]), |
700 |
["def", "ault", "name", "foo"]) |
701 |
|
702 |
|
703 |
class TestConstants(unittest.TestCase): |
704 |
def testPriority(self): |
705 |
self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()), |
706 |
set(constants.OP_PRIO_SUBMIT_VALID))
|
707 |
self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES), |
708 |
sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True)) |
709 |
|
710 |
|
711 |
class TestParseNicOption(unittest.TestCase): |
712 |
def test(self): |
713 |
self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]), |
714 |
[{ "link": "eth0", }]) |
715 |
self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]), |
716 |
[{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }]) |
717 |
|
718 |
def testErrors(self): |
719 |
for i in [None, "", "abc", "zero", "Hello World", "\0", []]: |
720 |
self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
|
721 |
[(i, { "link": "eth0", })]) |
722 |
self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
|
723 |
[("0", i)])
|
724 |
|
725 |
self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
|
726 |
[(0, { True: False, })]) |
727 |
|
728 |
self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
|
729 |
[(3, { "mode": [], })]) |
730 |
|
731 |
|
732 |
class TestFormatResultError(unittest.TestCase): |
733 |
def testNormal(self): |
734 |
for verbose in [False, True]: |
735 |
self.assertRaises(AssertionError, cli.FormatResultError, |
736 |
constants.RS_NORMAL, verbose) |
737 |
|
738 |
def testUnknown(self): |
739 |
for verbose in [False, True]: |
740 |
self.assertRaises(NotImplementedError, cli.FormatResultError, |
741 |
"#some!other!status#", verbose)
|
742 |
|
743 |
def test(self): |
744 |
for status in constants.RS_ALL: |
745 |
if status == constants.RS_NORMAL:
|
746 |
continue
|
747 |
|
748 |
self.assertNotEqual(cli.FormatResultError(status, False), |
749 |
cli.FormatResultError(status, True))
|
750 |
|
751 |
result = cli.FormatResultError(status, True)
|
752 |
self.assertTrue(result.startswith("(")) |
753 |
self.assertTrue(result.endswith(")")) |
754 |
|
755 |
|
756 |
if __name__ == '__main__': |
757 |
testutils.GanetiTestProgram() |