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"""
25 from cStringIO import StringIO
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
39 class TestParseTimespec(unittest.TestCase):
40 """Testing case for ParseTimespec"""
42 def testValidTimes(self):
43 """Test valid timespecs"""
50 ('1w', 60 * 60 * 24 * 7),
54 for value, expected_result in test_data:
55 self.failUnlessEqual(cli.ParseTimespec(value), expected_result)
57 def testInvalidTime(self):
58 """Test invalid timespecs"""
65 for value in test_data:
66 self.failUnlessRaises(OpPrereqError, cli.ParseTimespec, value)
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}
74 def testSplitKeyVal(self):
76 self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA), self.RESULT)
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,
84 def testEmptyData(self):
85 """Test how we handle splitting an empty string"""
86 self.failUnlessEqual(cli._SplitKeyVal("option", ""), {})
89 class TestIdentKeyVal(unittest.TestCase):
90 """Testing case for cli.check_ident_key_val"""
92 def testIdentKeyVal(self):
93 """Test identkeyval"""
95 return cli.check_ident_key_val("option", "opt", value)
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")
106 # Check negative numbers
107 self.assertEqual(cikv("-1:remove"), ("-1", {
110 self.assertEqual(cikv("-29447:add,size=4G"), ("-29447", {
114 for i in ["-:", "-"]:
115 self.assertEqual(cikv(i), ("", None))
118 class TestToStream(unittest.TestCase):
119 """Test the ToStream functions"""
129 cli._ToStream(buf, data)
130 self.failUnlessEqual(buf.getvalue(), data+'\n')
132 def testParams(self):
134 cli._ToStream(buf, "foo %s", 1)
135 self.failUnlessEqual(buf.getvalue(), "foo 1\n")
137 cli._ToStream(buf, "foo %s", (15,16))
138 self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n")
140 cli._ToStream(buf, "foo %s %s", "a", "b")
141 self.failUnlessEqual(buf.getvalue(), "foo a b\n")
144 class TestGenerateTable(unittest.TestCase):
145 HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
147 FIELDS1 = ["f1", "f2"]
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,
159 self.assertEqual(table, expected)
168 self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
169 None, None, "m", exp)
171 def testNoFields(self):
172 self._test(self.HEADERS, [], None, [[], []],
173 None, None, "m", ["", "", ""])
174 self._test(None, [], None, [[], []],
175 None, None, "m", ["", ""])
177 def testSeparator(self):
178 for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
180 "Field1%sField2" % sep,
185 self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
186 None, None, "m", exp)
188 def testNoHeader(self):
194 self._test(None, self.FIELDS1, None, self.DATA1,
195 None, None, "m", exp)
197 def testUnknownField(self):
207 self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
208 None, None, "m", exp)
210 def testNumfields(self):
211 fields = ["f1", "f2", "f3"]
218 "Field1 Field2 Field3",
223 self._test(self.HEADERS, fields, None, data,
224 ["f2", "f3"], None, "m", exp)
226 def testUnitfields(self):
228 "Field1 Field2 Field3",
235 "Field1:Field2:Field3",
241 for sep, expected in [(None, expnosep), (":", expsep)]:
242 fields = ["f1", "f2", "f3"]
248 self._test(self.HEADERS, fields, sep, data,
249 ["f2", "f3"], ["f3"], "h", expected)
251 def testUnusual(self):
261 self._test(self.HEADERS, ["f1", "f2"], None, data,
262 None, None, "m", exp)
265 class TestFormatQueryResult(unittest.TestCase):
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),
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, [{}, {}])],
292 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True),
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 [{}, {}]",
300 def testTimestampAndUnit(self):
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),
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)],
317 self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True),
320 "a 1024 %s" % utils.FormatTime(0),
321 "b 144996 %s" % utils.FormatTime(1291746295),
324 def testOverride(self):
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),
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)],
342 "cust": (utils.CommaJoin, False),
346 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True,
347 format_override=override),
351 "y 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
354 def testSeparator(self):
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),
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")],
371 for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]:
372 for header in [None, "Name%sCount%sDescription" % (sep, sep)]:
377 "instance1.example.com%s21125%sHello World!" % (sep, sep),
378 "mail.other.net%s-9000%sa,b,c" % (sep, sep),
381 self.assertEqual(cli.FormatQueryResult(response, separator=sep,
382 header=bool(header)),
383 (cli.QR_NORMAL, exp))
385 def testStatusWithUnknown(self):
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),
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)],
411 self.assertEqual(cli.FormatQueryResult(response, header=True,
412 separator="|", verbose=True),
414 "ID|unk|Unavail|NoData|OffLine",
415 "1|(unknown)|N||(offline)",
416 "2|(unknown)|(nodata)|x|(offline)",
417 "3|(unknown)|N|(unavail)|(offline)",
419 self.assertEqual(cli.FormatQueryResult(response, header=True,
420 separator="|", verbose=False),
422 "ID|unk|Unavail|NoData|OffLine",
428 def testNoData(self):
430 objects.QueryFieldDefinition(name="id", title="ID",
431 kind=constants.QFT_NUMBER),
432 objects.QueryFieldDefinition(name="name", title="Name",
433 kind=constants.QFT_TEXT),
436 response = objects.QueryResponse(fields=fields, data=[])
438 self.assertEqual(cli.FormatQueryResult(response, header=True),
439 (cli.QR_NORMAL, ["ID Name"]))
441 def testNoDataWithUnknown(self):
443 objects.QueryFieldDefinition(name="id", title="ID",
444 kind=constants.QFT_NUMBER),
445 objects.QueryFieldDefinition(name="unk", title="unk",
446 kind=constants.QFT_UNKNOWN),
449 response = objects.QueryResponse(fields=fields, data=[])
451 self.assertEqual(cli.FormatQueryResult(response, header=False),
452 (cli.QR_UNKNOWN, []))
454 def testStatus(self):
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),
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)],
475 self.assertEqual(cli.FormatQueryResult(response, header=False,
476 separator="|", verbose=True),
477 (cli.QR_INCOMPLETE, [
480 "3|N|(unavail)|(offline)",
482 self.assertEqual(cli.FormatQueryResult(response, header=False,
483 separator="|", verbose=False),
484 (cli.QR_INCOMPLETE, [
490 def testInvalidFieldType(self):
492 objects.QueryFieldDefinition(name="x", title="x",
493 kind="#some#other#type"),
496 response = objects.QueryResponse(fields=fields, data=[])
498 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
500 def testInvalidFieldStatus(self):
502 objects.QueryFieldDefinition(name="x", title="x",
503 kind=constants.QFT_TEXT),
506 response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
507 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
509 response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
510 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
512 def testEmptyFieldTitle(self):
514 objects.QueryFieldDefinition(name="x", title="",
515 kind=constants.QFT_TEXT),
518 response = objects.QueryResponse(fields=fields, data=[])
519 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
522 class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
523 def __init__(self, tc, job_id):
528 self._expect_notchanged = False
529 self._expect_log = []
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)
537 def AddWfjcResult(self, *args):
538 self._wfjcr.append(args)
540 def AddQueryJobsResult(self, *args):
541 self._jobstatus.append(args)
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)
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)
554 if result == constants.JOB_NOTCHANGED:
555 self._expect_notchanged = True
557 (_, logmsgs) = result
559 self._expect_log.extend(logmsgs)
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)
569 result = self._jobstatus.pop(0)
570 self.tc.assertEqual(len(fields), len(result))
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))
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
584 class TestGenericPollJob(testutils.GanetiTestCase):
585 def testSuccessWithLog(self):
587 cbs = _MockJobPollCb(self, job_id)
589 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
591 cbs.AddWfjcResult(None, None,
592 ((constants.JOB_STATUS_QUEUED, ), None))
594 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
595 constants.JOB_NOTCHANGED)
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")]))
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")]))
621 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
622 ((constants.JOB_STATUS_SUCCESS, ), None))
624 cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
625 [constants.OP_STATUS_SUCCESS,
626 constants.OP_STATUS_SUCCESS],
627 ["Hello World", "Foo man bar"])
629 self.assertEqual(["Hello World", "Foo man bar"],
630 cli.GenericPollJob(job_id, cbs, cbs))
633 def testJobLost(self):
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)
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)
655 def testError2(self):
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)
666 def testWeirdError(self):
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],
675 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
678 def testCancel(self):
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],
688 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
692 class TestFormatLogMessage(unittest.TestCase):
694 self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
697 self.assertRaises(TypeError, cli.FormatLogMessage,
698 constants.ELOG_MESSAGE, [1, 2, 3])
700 self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
703 class TestParseFields(unittest.TestCase):
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"]),
712 self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
713 ["def", "ault", "name", "foo"])
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))
724 class TestParseNicOption(unittest.TestCase):
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", }])
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,
738 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
739 [(0, { True: False, })])
741 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
742 [(3, { "mode": [], })])
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)
751 def testUnknown(self):
752 for verbose in [False, True]:
753 self.assertRaises(NotImplementedError, cli.FormatResultError,
754 "#some!other!status#", verbose)
757 for status in constants.RS_ALL:
758 if status == constants.RS_NORMAL:
761 self.assertNotEqual(cli.FormatResultError(status, False),
762 cli.FormatResultError(status, True))
764 result = cli.FormatResultError(status, True)
765 self.assertTrue(result.startswith("("))
766 self.assertTrue(result.endswith(")"))
769 class TestGetOnlineNodes(unittest.TestCase):
774 def AddQueryResult(self, *args):
775 self._query.append(args)
777 def CountPending(self):
778 return len(self._query)
780 def Query(self, res, fields, qfilter):
781 if res != constants.QR_NODE:
782 raise Exception("Querying wrong resource")
784 (exp_fields, check_filter, result) = self._query.pop(0)
786 if exp_fields != fields:
787 raise Exception("Expected fields %s, got %s" % (exp_fields, fields))
789 if not (qfilter is None or check_filter(qfilter)):
790 raise Exception("Filter doesn't match expectations")
792 return objects.QueryResponse(fields=None, data=result)
795 cl = self._FakeClient()
797 cl.AddQueryResult(["name", "offline", "sip"], None, [])
798 self.assertEqual(cli.GetOnlineNodes(None, cl=cl), [])
799 self.assertEqual(cl.CountPending(), 0)
801 def testNoSpecialFilter(self):
802 cl = self._FakeClient()
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")],
812 self.assertEqual(cli.GetOnlineNodes(None, cl=cl),
813 ["master.example.com", "node2.example.com"])
814 self.assertEqual(cl.CountPending(), 0)
816 def testNoMaster(self):
817 cl = self._FakeClient()
819 def _CheckFilter(qfilter):
820 self.assertEqual(qfilter, [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
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")],
828 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, filter_master=True),
829 ["node2.example.com"])
830 self.assertEqual(cl.CountPending(), 0)
832 def testSecondaryIpAddress(self):
833 cl = self._FakeClient()
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")],
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)
847 def testNoMasterFilterNodeName(self):
848 cl = self._FakeClient()
850 def _CheckFilter(qfilter):
851 self.assertEqual(qfilter,
853 [qlang.OP_OR] + [[qlang.OP_EQUAL, "name", name]
854 for name in ["node2", "node3"]],
855 [qlang.OP_NOT, [qlang.OP_TRUE, "master"]]])
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")],
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)
871 def testOfflineNodes(self):
872 cl = self._FakeClient()
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")],
885 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nowarn=True),
886 ["master.example.com"])
887 self.assertEqual(cl.CountPending(), 0)
889 def testNodeGroup(self):
890 cl = self._FakeClient()
892 def _CheckFilter(qfilter):
893 self.assertEqual(qfilter,
894 [qlang.OP_OR, [qlang.OP_EQUAL, "group", "foobar"],
895 [qlang.OP_EQUAL, "group.uuid", "foobar"]])
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")],
906 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nodegroup="foobar"),
907 ["master.example.com", "node3.example.com"])
908 self.assertEqual(cl.CountPending(), 0)
911 if __name__ == '__main__':
912 testutils.GanetiTestProgram()