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