4 # Copyright (C) 2008, 2011 Google Inc.
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.
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.
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
22 """Script for unittesting the cli module"""
26 from cStringIO import StringIO
31 from ganeti import constants
32 from ganeti import cli
33 from ganeti import errors
34 from ganeti import utils
35 from ganeti import objects
36 from ganeti import qlang
37 from ganeti.errors import OpPrereqError, ParameterError
40 class TestParseTimespec(unittest.TestCase):
41 """Testing case for ParseTimespec"""
43 def testValidTimes(self):
44 """Test valid timespecs"""
51 ("1w", 60 * 60 * 24 * 7),
55 for value, expected_result in test_data:
56 self.failUnlessEqual(cli.ParseTimespec(value), expected_result)
58 def testInvalidTime(self):
59 """Test invalid timespecs"""
66 for value in test_data:
67 self.failUnlessRaises(OpPrereqError, cli.ParseTimespec, value)
70 class TestSplitKeyVal(unittest.TestCase):
71 """Testing case for cli._SplitKeyVal"""
72 DATA = "a=b,c,no_d,-e"
73 RESULT = {"a": "b", "c": True, "d": False, "e": None}
75 def testSplitKeyVal(self):
77 self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA), self.RESULT)
79 def testDuplicateParam(self):
80 """Test duplicate parameters"""
81 for data in ("a=1,a=2", "a,no_a"):
82 self.failUnlessRaises(ParameterError, cli._SplitKeyVal,
85 def testEmptyData(self):
86 """Test how we handle splitting an empty string"""
87 self.failUnlessEqual(cli._SplitKeyVal("option", ""), {})
90 class TestIdentKeyVal(unittest.TestCase):
91 """Testing case for cli.check_ident_key_val"""
93 def testIdentKeyVal(self):
94 """Test identkeyval"""
96 return cli.check_ident_key_val("option", "opt", value)
98 self.assertEqual(cikv("foo:bar"), ("foo", {"bar": True}))
99 self.assertEqual(cikv("foo:bar=baz"), ("foo", {"bar": "baz"}))
100 self.assertEqual(cikv("bar:b=c,c=a"), ("bar", {"b": "c", "c": "a"}))
101 self.assertEqual(cikv("no_bar"), ("bar", False))
102 self.assertRaises(ParameterError, cikv, "no_bar:foo")
103 self.assertRaises(ParameterError, cikv, "no_bar:foo=baz")
104 self.assertEqual(cikv("-foo"), ("foo", None))
105 self.assertRaises(ParameterError, cikv, "-foo:a=c")
107 # Check negative numbers
108 self.assertEqual(cikv("-1:remove"), ("-1", {
111 self.assertEqual(cikv("-29447:add,size=4G"), ("-29447", {
115 for i in ["-:", "-"]:
116 self.assertEqual(cikv(i), ("", None))
119 class TestToStream(unittest.TestCase):
120 """Test the ToStream functions"""
130 cli._ToStream(buf, data)
131 self.failUnlessEqual(buf.getvalue(), data + "\n")
133 def testParams(self):
135 cli._ToStream(buf, "foo %s", 1)
136 self.failUnlessEqual(buf.getvalue(), "foo 1\n")
138 cli._ToStream(buf, "foo %s", (15,16))
139 self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n")
141 cli._ToStream(buf, "foo %s %s", "a", "b")
142 self.failUnlessEqual(buf.getvalue(), "foo a b\n")
145 class TestGenerateTable(unittest.TestCase):
146 HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
148 FIELDS1 = ["f1", "f2"]
155 def _test(self, headers, fields, separator, data,
156 numfields, unitfields, units, expected):
157 table = cli.GenerateTable(headers, fields, separator, data,
158 numfields=numfields, unitfields=unitfields,
160 self.assertEqual(table, expected)
169 self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
170 None, None, "m", exp)
172 def testNoFields(self):
173 self._test(self.HEADERS, [], None, [[], []],
174 None, None, "m", ["", "", ""])
175 self._test(None, [], None, [[], []],
176 None, None, "m", ["", ""])
178 def testSeparator(self):
179 for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
181 "Field1%sField2" % sep,
186 self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
187 None, None, "m", exp)
189 def testNoHeader(self):
195 self._test(None, self.FIELDS1, None, self.DATA1,
196 None, None, "m", exp)
198 def testUnknownField(self):
208 self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
209 None, None, "m", exp)
211 def testNumfields(self):
212 fields = ["f1", "f2", "f3"]
219 "Field1 Field2 Field3",
224 self._test(self.HEADERS, fields, None, data,
225 ["f2", "f3"], None, "m", exp)
227 def testUnitfields(self):
229 "Field1 Field2 Field3",
236 "Field1:Field2:Field3",
242 for sep, expected in [(None, expnosep), (":", expsep)]:
243 fields = ["f1", "f2", "f3"]
249 self._test(self.HEADERS, fields, sep, data,
250 ["f2", "f3"], ["f3"], "h", expected)
252 def testUnusual(self):
262 self._test(self.HEADERS, ["f1", "f2"], None, data,
263 None, None, "m", exp)
266 class TestFormatQueryResult(unittest.TestCase):
269 objects.QueryFieldDefinition(name="name", title="Name",
270 kind=constants.QFT_TEXT),
271 objects.QueryFieldDefinition(name="size", title="Size",
272 kind=constants.QFT_NUMBER),
273 objects.QueryFieldDefinition(name="act", title="Active",
274 kind=constants.QFT_BOOL),
275 objects.QueryFieldDefinition(name="mem", title="Memory",
276 kind=constants.QFT_UNIT),
277 objects.QueryFieldDefinition(name="other", title="SomeList",
278 kind=constants.QFT_OTHER),
281 response = objects.QueryResponse(fields=fields, data=[
282 [(constants.RS_NORMAL, "nodeA"), (constants.RS_NORMAL, 128),
283 (constants.RS_NORMAL, False), (constants.RS_NORMAL, 1468006),
284 (constants.RS_NORMAL, [])],
285 [(constants.RS_NORMAL, "other"), (constants.RS_NORMAL, 512),
286 (constants.RS_NORMAL, True), (constants.RS_NORMAL, 16),
287 (constants.RS_NORMAL, [1, 2, 3])],
288 [(constants.RS_NORMAL, "xyz"), (constants.RS_NORMAL, 1024),
289 (constants.RS_NORMAL, True), (constants.RS_NORMAL, 4096),
290 (constants.RS_NORMAL, [{}, {}])],
293 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True),
295 "Name Size Active Memory SomeList",
296 "nodeA 128 N 1.4T []",
297 "other 512 Y 16M [1, 2, 3]",
298 "xyz 1024 Y 4.0G [{}, {}]",
301 def testTimestampAndUnit(self):
303 objects.QueryFieldDefinition(name="name", title="Name",
304 kind=constants.QFT_TEXT),
305 objects.QueryFieldDefinition(name="size", title="Size",
306 kind=constants.QFT_UNIT),
307 objects.QueryFieldDefinition(name="mtime", title="ModTime",
308 kind=constants.QFT_TIMESTAMP),
311 response = objects.QueryResponse(fields=fields, data=[
312 [(constants.RS_NORMAL, "a"), (constants.RS_NORMAL, 1024),
313 (constants.RS_NORMAL, 0)],
314 [(constants.RS_NORMAL, "b"), (constants.RS_NORMAL, 144996),
315 (constants.RS_NORMAL, 1291746295)],
318 self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True),
321 "a 1024 %s" % utils.FormatTime(0),
322 "b 144996 %s" % utils.FormatTime(1291746295),
325 def testOverride(self):
327 objects.QueryFieldDefinition(name="name", title="Name",
328 kind=constants.QFT_TEXT),
329 objects.QueryFieldDefinition(name="cust", title="Custom",
330 kind=constants.QFT_OTHER),
331 objects.QueryFieldDefinition(name="xt", title="XTime",
332 kind=constants.QFT_TIMESTAMP),
335 response = objects.QueryResponse(fields=fields, data=[
336 [(constants.RS_NORMAL, "x"), (constants.RS_NORMAL, ["a", "b", "c"]),
337 (constants.RS_NORMAL, 1234)],
338 [(constants.RS_NORMAL, "y"), (constants.RS_NORMAL, range(10)),
339 (constants.RS_NORMAL, 1291746295)],
343 "cust": (utils.CommaJoin, False),
347 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True,
348 format_override=override),
352 "y 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
355 def testSeparator(self):
357 objects.QueryFieldDefinition(name="name", title="Name",
358 kind=constants.QFT_TEXT),
359 objects.QueryFieldDefinition(name="count", title="Count",
360 kind=constants.QFT_NUMBER),
361 objects.QueryFieldDefinition(name="desc", title="Description",
362 kind=constants.QFT_TEXT),
365 response = objects.QueryResponse(fields=fields, data=[
366 [(constants.RS_NORMAL, "instance1.example.com"),
367 (constants.RS_NORMAL, 21125), (constants.RS_NORMAL, "Hello World!")],
368 [(constants.RS_NORMAL, "mail.other.net"),
369 (constants.RS_NORMAL, -9000), (constants.RS_NORMAL, "a,b,c")],
372 for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]:
373 for header in [None, "Name%sCount%sDescription" % (sep, sep)]:
378 "instance1.example.com%s21125%sHello World!" % (sep, sep),
379 "mail.other.net%s-9000%sa,b,c" % (sep, sep),
382 self.assertEqual(cli.FormatQueryResult(response, separator=sep,
383 header=bool(header)),
384 (cli.QR_NORMAL, exp))
386 def testStatusWithUnknown(self):
388 objects.QueryFieldDefinition(name="id", title="ID",
389 kind=constants.QFT_NUMBER),
390 objects.QueryFieldDefinition(name="unk", title="unk",
391 kind=constants.QFT_UNKNOWN),
392 objects.QueryFieldDefinition(name="unavail", title="Unavail",
393 kind=constants.QFT_BOOL),
394 objects.QueryFieldDefinition(name="nodata", title="NoData",
395 kind=constants.QFT_TEXT),
396 objects.QueryFieldDefinition(name="offline", title="OffLine",
397 kind=constants.QFT_TEXT),
400 response = objects.QueryResponse(fields=fields, data=[
401 [(constants.RS_NORMAL, 1), (constants.RS_UNKNOWN, None),
402 (constants.RS_NORMAL, False), (constants.RS_NORMAL, ""),
403 (constants.RS_OFFLINE, None)],
404 [(constants.RS_NORMAL, 2), (constants.RS_UNKNOWN, None),
405 (constants.RS_NODATA, None), (constants.RS_NORMAL, "x"),
406 (constants.RS_OFFLINE, None)],
407 [(constants.RS_NORMAL, 3), (constants.RS_UNKNOWN, None),
408 (constants.RS_NORMAL, False), (constants.RS_UNAVAIL, None),
409 (constants.RS_OFFLINE, None)],
412 self.assertEqual(cli.FormatQueryResult(response, header=True,
413 separator="|", verbose=True),
415 "ID|unk|Unavail|NoData|OffLine",
416 "1|(unknown)|N||(offline)",
417 "2|(unknown)|(nodata)|x|(offline)",
418 "3|(unknown)|N|(unavail)|(offline)",
420 self.assertEqual(cli.FormatQueryResult(response, header=True,
421 separator="|", verbose=False),
423 "ID|unk|Unavail|NoData|OffLine",
429 def testNoData(self):
431 objects.QueryFieldDefinition(name="id", title="ID",
432 kind=constants.QFT_NUMBER),
433 objects.QueryFieldDefinition(name="name", title="Name",
434 kind=constants.QFT_TEXT),
437 response = objects.QueryResponse(fields=fields, data=[])
439 self.assertEqual(cli.FormatQueryResult(response, header=True),
440 (cli.QR_NORMAL, ["ID Name"]))
442 def testNoDataWithUnknown(self):
444 objects.QueryFieldDefinition(name="id", title="ID",
445 kind=constants.QFT_NUMBER),
446 objects.QueryFieldDefinition(name="unk", title="unk",
447 kind=constants.QFT_UNKNOWN),
450 response = objects.QueryResponse(fields=fields, data=[])
452 self.assertEqual(cli.FormatQueryResult(response, header=False),
453 (cli.QR_UNKNOWN, []))
455 def testStatus(self):
457 objects.QueryFieldDefinition(name="id", title="ID",
458 kind=constants.QFT_NUMBER),
459 objects.QueryFieldDefinition(name="unavail", title="Unavail",
460 kind=constants.QFT_BOOL),
461 objects.QueryFieldDefinition(name="nodata", title="NoData",
462 kind=constants.QFT_TEXT),
463 objects.QueryFieldDefinition(name="offline", title="OffLine",
464 kind=constants.QFT_TEXT),
467 response = objects.QueryResponse(fields=fields, data=[
468 [(constants.RS_NORMAL, 1), (constants.RS_NORMAL, False),
469 (constants.RS_NORMAL, ""), (constants.RS_OFFLINE, None)],
470 [(constants.RS_NORMAL, 2), (constants.RS_NODATA, None),
471 (constants.RS_NORMAL, "x"), (constants.RS_NORMAL, "abc")],
472 [(constants.RS_NORMAL, 3), (constants.RS_NORMAL, False),
473 (constants.RS_UNAVAIL, None), (constants.RS_OFFLINE, None)],
476 self.assertEqual(cli.FormatQueryResult(response, header=False,
477 separator="|", verbose=True),
478 (cli.QR_INCOMPLETE, [
481 "3|N|(unavail)|(offline)",
483 self.assertEqual(cli.FormatQueryResult(response, header=False,
484 separator="|", verbose=False),
485 (cli.QR_INCOMPLETE, [
491 def testInvalidFieldType(self):
493 objects.QueryFieldDefinition(name="x", title="x",
494 kind="#some#other#type"),
497 response = objects.QueryResponse(fields=fields, data=[])
499 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
501 def testInvalidFieldStatus(self):
503 objects.QueryFieldDefinition(name="x", title="x",
504 kind=constants.QFT_TEXT),
507 response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
508 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
510 response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
511 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
513 def testEmptyFieldTitle(self):
515 objects.QueryFieldDefinition(name="x", title="",
516 kind=constants.QFT_TEXT),
519 response = objects.QueryResponse(fields=fields, data=[])
520 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
523 class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
524 def __init__(self, tc, job_id):
529 self._expect_notchanged = False
530 self._expect_log = []
532 def CheckEmpty(self):
533 self.tc.assertFalse(self._wfjcr)
534 self.tc.assertFalse(self._jobstatus)
535 self.tc.assertFalse(self._expect_notchanged)
536 self.tc.assertFalse(self._expect_log)
538 def AddWfjcResult(self, *args):
539 self._wfjcr.append(args)
541 def AddQueryJobsResult(self, *args):
542 self._jobstatus.append(args)
544 def WaitForJobChangeOnce(self, job_id, fields,
545 prev_job_info, prev_log_serial):
546 self.tc.assertEqual(job_id, self.job_id)
547 self.tc.assertEqualValues(fields, ["status"])
548 self.tc.assertFalse(self._expect_notchanged)
549 self.tc.assertFalse(self._expect_log)
551 (exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0)
552 self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
553 self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
555 if result == constants.JOB_NOTCHANGED:
556 self._expect_notchanged = True
558 (_, logmsgs) = result
560 self._expect_log.extend(logmsgs)
564 def QueryJobs(self, job_ids, fields):
565 self.tc.assertEqual(job_ids, [self.job_id])
566 self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"])
567 self.tc.assertFalse(self._expect_notchanged)
568 self.tc.assertFalse(self._expect_log)
570 result = self._jobstatus.pop(0)
571 self.tc.assertEqual(len(fields), len(result))
574 def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
575 self.tc.assertEqual(job_id, self.job_id)
576 self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
577 self._expect_log.pop(0))
579 def ReportNotChanged(self, job_id, status):
580 self.tc.assertEqual(job_id, self.job_id)
581 self.tc.assert_(self._expect_notchanged)
582 self._expect_notchanged = False
585 class TestGenericPollJob(testutils.GanetiTestCase):
586 def testSuccessWithLog(self):
588 cbs = _MockJobPollCb(self, job_id)
590 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
592 cbs.AddWfjcResult(None, None,
593 ((constants.JOB_STATUS_QUEUED, ), None))
595 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
596 constants.JOB_NOTCHANGED)
598 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
599 ((constants.JOB_STATUS_RUNNING, ),
600 [(1, utils.SplitTime(1273491611.0),
601 constants.ELOG_MESSAGE, "Step 1"),
602 (2, utils.SplitTime(1273491615.9),
603 constants.ELOG_MESSAGE, "Step 2"),
604 (3, utils.SplitTime(1273491625.02),
605 constants.ELOG_MESSAGE, "Step 3"),
606 (4, utils.SplitTime(1273491635.05),
607 constants.ELOG_MESSAGE, "Step 4"),
608 (37, utils.SplitTime(1273491645.0),
609 constants.ELOG_MESSAGE, "Step 5"),
610 (203, utils.SplitTime(127349155.0),
611 constants.ELOG_MESSAGE, "Step 6")]))
613 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
614 ((constants.JOB_STATUS_RUNNING, ),
615 [(300, utils.SplitTime(1273491711.01),
616 constants.ELOG_MESSAGE, "Step X"),
617 (302, utils.SplitTime(1273491815.8),
618 constants.ELOG_MESSAGE, "Step Y"),
619 (303, utils.SplitTime(1273491925.32),
620 constants.ELOG_MESSAGE, "Step Z")]))
622 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
623 ((constants.JOB_STATUS_SUCCESS, ), None))
625 cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
626 [constants.OP_STATUS_SUCCESS,
627 constants.OP_STATUS_SUCCESS],
628 ["Hello World", "Foo man bar"])
630 self.assertEqual(["Hello World", "Foo man bar"],
631 cli.GenericPollJob(job_id, cbs, cbs))
634 def testJobLost(self):
637 cbs = _MockJobPollCb(self, job_id)
638 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
639 cbs.AddWfjcResult(None, None, None)
640 self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
646 cbs = _MockJobPollCb(self, job_id)
647 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
648 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
649 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
650 [constants.OP_STATUS_SUCCESS,
651 constants.OP_STATUS_ERROR],
652 ["Hello World", "Error code 123"])
653 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
656 def testError2(self):
659 cbs = _MockJobPollCb(self, job_id)
660 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
661 encexc = errors.EncodeException(errors.LockError("problem"))
662 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
663 [constants.OP_STATUS_ERROR], [encexc])
664 self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
667 def testWeirdError(self):
670 cbs = _MockJobPollCb(self, job_id)
671 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
672 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
673 [constants.OP_STATUS_RUNNING,
674 constants.OP_STATUS_RUNNING],
676 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
679 def testCancel(self):
682 cbs = _MockJobPollCb(self, job_id)
683 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
684 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None))
685 cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING,
686 [constants.OP_STATUS_CANCELING,
687 constants.OP_STATUS_CANCELING],
689 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
693 class TestFormatLogMessage(unittest.TestCase):
695 self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
698 self.assertRaises(TypeError, cli.FormatLogMessage,
699 constants.ELOG_MESSAGE, [1, 2, 3])
701 self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
704 class TestParseFields(unittest.TestCase):
706 self.assertEqual(cli.ParseFields(None, []), [])
707 self.assertEqual(cli.ParseFields("name,foo,hello", []),
708 ["name", "foo", "hello"])
709 self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]),
710 ["def", "ault", "fields", "here"])
711 self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]),
713 self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
714 ["def", "ault", "name", "foo"])
717 class TestConstants(unittest.TestCase):
718 def testPriority(self):
719 self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()),
720 set(constants.OP_PRIO_SUBMIT_VALID))
721 self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES),
722 sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True))
725 class TestParseNicOption(unittest.TestCase):
727 self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]),
728 [{ "link": "eth0", }])
729 self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]),
730 [{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }])
732 def testErrors(self):
733 for i in [None, "", "abc", "zero", "Hello World", "\0", []]:
734 self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
735 [(i, { "link": "eth0", })])
736 self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
739 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
740 [(0, { True: False, })])
742 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
743 [(3, { "mode": [], })])
746 class TestFormatResultError(unittest.TestCase):
747 def testNormal(self):
748 for verbose in [False, True]:
749 self.assertRaises(AssertionError, cli.FormatResultError,
750 constants.RS_NORMAL, verbose)
752 def testUnknown(self):
753 for verbose in [False, True]:
754 self.assertRaises(NotImplementedError, cli.FormatResultError,
755 "#some!other!status#", verbose)
758 for status in constants.RS_ALL:
759 if status == constants.RS_NORMAL:
762 self.assertNotEqual(cli.FormatResultError(status, False),
763 cli.FormatResultError(status, True))
765 result = cli.FormatResultError(status, True)
766 self.assertTrue(result.startswith("("))
767 self.assertTrue(result.endswith(")"))
770 class TestGetOnlineNodes(unittest.TestCase):
775 def AddQueryResult(self, *args):
776 self._query.append(args)
778 def CountPending(self):
779 return len(self._query)
781 def Query(self, res, fields, qfilter):
782 if res != constants.QR_NODE:
783 raise Exception("Querying wrong resource")
785 (exp_fields, check_filter, result) = self._query.pop(0)
787 if exp_fields != fields:
788 raise Exception("Expected fields %s, got %s" % (exp_fields, fields))
790 if not (qfilter is None or check_filter(qfilter)):
791 raise Exception("Filter doesn't match expectations")
793 return objects.QueryResponse(fields=None, data=result)
796 cl = self._FakeClient()
798 cl.AddQueryResult(["name", "offline", "sip"], None, [])
799 self.assertEqual(cli.GetOnlineNodes(None, cl=cl), [])
800 self.assertEqual(cl.CountPending(), 0)
802 def testNoSpecialFilter(self):
803 cl = self._FakeClient()
805 cl.AddQueryResult(["name", "offline", "sip"], None, [
806 [(constants.RS_NORMAL, "master.example.com"),
807 (constants.RS_NORMAL, False),
808 (constants.RS_NORMAL, "192.0.2.1")],
809 [(constants.RS_NORMAL, "node2.example.com"),
810 (constants.RS_NORMAL, False),
811 (constants.RS_NORMAL, "192.0.2.2")],
813 self.assertEqual(cli.GetOnlineNodes(None, cl=cl),
814 ["master.example.com", "node2.example.com"])
815 self.assertEqual(cl.CountPending(), 0)
817 def testNoMaster(self):
818 cl = self._FakeClient()
820 def _CheckFilter(qfilter):
821 self.assertEqual(qfilter, [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
824 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
825 [(constants.RS_NORMAL, "node2.example.com"),
826 (constants.RS_NORMAL, False),
827 (constants.RS_NORMAL, "192.0.2.2")],
829 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, filter_master=True),
830 ["node2.example.com"])
831 self.assertEqual(cl.CountPending(), 0)
833 def testSecondaryIpAddress(self):
834 cl = self._FakeClient()
836 cl.AddQueryResult(["name", "offline", "sip"], None, [
837 [(constants.RS_NORMAL, "master.example.com"),
838 (constants.RS_NORMAL, False),
839 (constants.RS_NORMAL, "192.0.2.1")],
840 [(constants.RS_NORMAL, "node2.example.com"),
841 (constants.RS_NORMAL, False),
842 (constants.RS_NORMAL, "192.0.2.2")],
844 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, secondary_ips=True),
845 ["192.0.2.1", "192.0.2.2"])
846 self.assertEqual(cl.CountPending(), 0)
848 def testNoMasterFilterNodeName(self):
849 cl = self._FakeClient()
851 def _CheckFilter(qfilter):
852 self.assertEqual(qfilter,
854 [qlang.OP_OR] + [[qlang.OP_EQUAL, "name", name]
855 for name in ["node2", "node3"]],
856 [qlang.OP_NOT, [qlang.OP_TRUE, "master"]]])
859 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
860 [(constants.RS_NORMAL, "node2.example.com"),
861 (constants.RS_NORMAL, False),
862 (constants.RS_NORMAL, "192.0.2.12")],
863 [(constants.RS_NORMAL, "node3.example.com"),
864 (constants.RS_NORMAL, False),
865 (constants.RS_NORMAL, "192.0.2.13")],
867 self.assertEqual(cli.GetOnlineNodes(["node2", "node3"], cl=cl,
868 secondary_ips=True, filter_master=True),
869 ["192.0.2.12", "192.0.2.13"])
870 self.assertEqual(cl.CountPending(), 0)
872 def testOfflineNodes(self):
873 cl = self._FakeClient()
875 cl.AddQueryResult(["name", "offline", "sip"], None, [
876 [(constants.RS_NORMAL, "master.example.com"),
877 (constants.RS_NORMAL, False),
878 (constants.RS_NORMAL, "192.0.2.1")],
879 [(constants.RS_NORMAL, "node2.example.com"),
880 (constants.RS_NORMAL, True),
881 (constants.RS_NORMAL, "192.0.2.2")],
882 [(constants.RS_NORMAL, "node3.example.com"),
883 (constants.RS_NORMAL, True),
884 (constants.RS_NORMAL, "192.0.2.3")],
886 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nowarn=True),
887 ["master.example.com"])
888 self.assertEqual(cl.CountPending(), 0)
890 def testNodeGroup(self):
891 cl = self._FakeClient()
893 def _CheckFilter(qfilter):
894 self.assertEqual(qfilter,
895 [qlang.OP_OR, [qlang.OP_EQUAL, "group", "foobar"],
896 [qlang.OP_EQUAL, "group.uuid", "foobar"]])
899 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
900 [(constants.RS_NORMAL, "master.example.com"),
901 (constants.RS_NORMAL, False),
902 (constants.RS_NORMAL, "192.0.2.1")],
903 [(constants.RS_NORMAL, "node3.example.com"),
904 (constants.RS_NORMAL, False),
905 (constants.RS_NORMAL, "192.0.2.3")],
907 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nodegroup="foobar"),
908 ["master.example.com", "node3.example.com"])
909 self.assertEqual(cl.CountPending(), 0)
912 class TestFormatTimestamp(unittest.TestCase):
914 self.assertEqual(cli.FormatTimestamp((0, 1)),
915 time.strftime("%F %T", time.localtime(0)) + ".000001")
916 self.assertEqual(cli.FormatTimestamp((1332944009, 17376)),
917 (time.strftime("%F %T", time.localtime(1332944009)) +
921 for i in [0, [], {}, "", [1]]:
922 self.assertEqual(cli.FormatTimestamp(i), "?")
925 class TestFormatUsage(unittest.TestCase):
927 binary = "gnt-unittest"
930 (NotImplemented, NotImplemented, NotImplemented, NotImplemented,
933 (NotImplemented, NotImplemented, NotImplemented, NotImplemented,
934 "Hello World," * 10),
936 (NotImplemented, NotImplemented, NotImplemented, NotImplemented,
937 "Another description"),
940 self.assertEqual(list(cli._FormatUsage(binary, commands)), [
941 "Usage: gnt-unittest {command} [options...] [argument...]",
942 "gnt-unittest <command> --help to see details, or man gnt-unittest",
945 (" bbb - Hello World,Hello World,Hello World,Hello World,Hello"
947 " World,Hello World,Hello World,Hello World,Hello World,",
948 " cmdA - description of A",
949 " longname - Another description",
954 class TestParseArgs(unittest.TestCase):
955 def testNoArguments(self):
956 for argv in [[], ["gnt-unittest"]]:
958 cli._ParseArgs("gnt-unittest", argv, {}, {}, set())
959 except cli._ShowUsage, err:
960 self.assertTrue(err.exit_error)
962 self.fail("Did not raise exception")
964 def testVersion(self):
965 for argv in [["test", "--version"], ["test", "--version", "somethingelse"]]:
967 cli._ParseArgs("test", argv, {}, {}, set())
968 except cli._ShowVersion:
971 self.fail("Did not raise exception")
974 for argv in [["test", "--help"], ["test", "--help", "somethingelse"]]:
976 cli._ParseArgs("test", argv, {}, {}, set())
977 except cli._ShowUsage, err:
978 self.assertFalse(err.exit_error)
980 self.fail("Did not raise exception")
982 def testUnknownCommandOrAlias(self):
983 for argv in [["test", "list"], ["test", "somethingelse", "--help"]]:
985 cli._ParseArgs("test", argv, {}, {}, set())
986 except cli._ShowUsage, err:
987 self.assertTrue(err.exit_error)
989 self.fail("Did not raise exception")
991 def testInvalidAliasList(self):
993 "list": NotImplemented,
994 "foo": NotImplemented,
997 "list": NotImplemented,
998 "foo": NotImplemented,
1000 assert sorted(cmd.keys()) == sorted(aliases.keys())
1001 self.assertRaises(AssertionError, cli._ParseArgs, "test",
1002 ["test", "list"], cmd, aliases, set())
1004 def testAliasForNonExistantCommand(self):
1007 "list": NotImplemented,
1009 self.assertRaises(errors.ProgrammerError, cli._ParseArgs, "test",
1010 ["test", "list"], cmd, aliases, set())
1013 class TestQftNames(unittest.TestCase):
1014 def testComplete(self):
1015 self.assertEqual(frozenset(cli._QFT_NAMES), constants.QFT_ALL)
1017 def testUnique(self):
1018 lcnames = map(lambda s: s.lower(), cli._QFT_NAMES.values())
1019 self.assertFalse(utils.FindDuplicates(lcnames))
1021 def testUppercase(self):
1022 for name in cli._QFT_NAMES.values():
1023 self.assertEqual(name[0], name[0].upper())
1026 class TestFieldDescValues(unittest.TestCase):
1027 def testKnownKind(self):
1028 fdef = objects.QueryFieldDefinition(name="aname",
1030 kind=constants.QFT_TEXT,
1032 self.assertEqual(cli._FieldDescValues(fdef),
1033 ["aname", "Text", "Atitle", "aaa doc aaa"])
1035 def testUnknownKind(self):
1038 self.assertFalse(kind in constants.QFT_ALL)
1039 self.assertFalse(kind in cli._QFT_NAMES)
1041 fdef = objects.QueryFieldDefinition(name="zname", title="Ztitle",
1042 kind=kind, doc="zzz doc zzz")
1043 self.assertEqual(cli._FieldDescValues(fdef),
1044 ["zname", kind, "Ztitle", "zzz doc zzz"])
1047 if __name__ == "__main__":
1048 testutils.GanetiTestProgram()