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", ""), {})
88 class TestIdentKeyVal(unittest.TestCase):
89 """Testing case for cli.check_ident_key_val"""
91 def testIdentKeyVal(self):
92 """Test identkeyval"""
94 return cli.check_ident_key_val("option", "opt", value)
96 self.assertEqual(cikv("foo:bar"), ("foo", {"bar": True}))
97 self.assertEqual(cikv("foo:bar=baz"), ("foo", {"bar": "baz"}))
98 self.assertEqual(cikv("bar:b=c,c=a"), ("bar", {"b": "c", "c": "a"}))
99 self.assertEqual(cikv("no_bar"), ("bar", False))
100 self.assertRaises(ParameterError, cikv, "no_bar:foo")
101 self.assertRaises(ParameterError, cikv, "no_bar:foo=baz")
102 self.assertEqual(cikv("-foo"), ("foo", None))
103 self.assertRaises(ParameterError, cikv, "-foo:a=c")
106 class TestToStream(unittest.TestCase):
107 """Test the ToStream functions"""
117 cli._ToStream(buf, data)
118 self.failUnlessEqual(buf.getvalue(), data+'\n')
120 def testParams(self):
122 cli._ToStream(buf, "foo %s", 1)
123 self.failUnlessEqual(buf.getvalue(), "foo 1\n")
125 cli._ToStream(buf, "foo %s", (15,16))
126 self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n")
128 cli._ToStream(buf, "foo %s %s", "a", "b")
129 self.failUnlessEqual(buf.getvalue(), "foo a b\n")
132 class TestGenerateTable(unittest.TestCase):
133 HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
135 FIELDS1 = ["f1", "f2"]
142 def _test(self, headers, fields, separator, data,
143 numfields, unitfields, units, expected):
144 table = cli.GenerateTable(headers, fields, separator, data,
145 numfields=numfields, unitfields=unitfields,
147 self.assertEqual(table, expected)
156 self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
157 None, None, "m", exp)
159 def testNoFields(self):
160 self._test(self.HEADERS, [], None, [[], []],
161 None, None, "m", ["", "", ""])
162 self._test(None, [], None, [[], []],
163 None, None, "m", ["", ""])
165 def testSeparator(self):
166 for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
168 "Field1%sField2" % sep,
173 self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
174 None, None, "m", exp)
176 def testNoHeader(self):
182 self._test(None, self.FIELDS1, None, self.DATA1,
183 None, None, "m", exp)
185 def testUnknownField(self):
195 self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
196 None, None, "m", exp)
198 def testNumfields(self):
199 fields = ["f1", "f2", "f3"]
206 "Field1 Field2 Field3",
211 self._test(self.HEADERS, fields, None, data,
212 ["f2", "f3"], None, "m", exp)
214 def testUnitfields(self):
216 "Field1 Field2 Field3",
223 "Field1:Field2:Field3",
229 for sep, expected in [(None, expnosep), (":", expsep)]:
230 fields = ["f1", "f2", "f3"]
236 self._test(self.HEADERS, fields, sep, data,
237 ["f2", "f3"], ["f3"], "h", expected)
239 def testUnusual(self):
249 self._test(self.HEADERS, ["f1", "f2"], None, data,
250 None, None, "m", exp)
253 class TestFormatQueryResult(unittest.TestCase):
256 objects.QueryFieldDefinition(name="name", title="Name",
257 kind=constants.QFT_TEXT),
258 objects.QueryFieldDefinition(name="size", title="Size",
259 kind=constants.QFT_NUMBER),
260 objects.QueryFieldDefinition(name="act", title="Active",
261 kind=constants.QFT_BOOL),
262 objects.QueryFieldDefinition(name="mem", title="Memory",
263 kind=constants.QFT_UNIT),
264 objects.QueryFieldDefinition(name="other", title="SomeList",
265 kind=constants.QFT_OTHER),
268 response = objects.QueryResponse(fields=fields, data=[
269 [(constants.RS_NORMAL, "nodeA"), (constants.RS_NORMAL, 128),
270 (constants.RS_NORMAL, False), (constants.RS_NORMAL, 1468006),
271 (constants.RS_NORMAL, [])],
272 [(constants.RS_NORMAL, "other"), (constants.RS_NORMAL, 512),
273 (constants.RS_NORMAL, True), (constants.RS_NORMAL, 16),
274 (constants.RS_NORMAL, [1, 2, 3])],
275 [(constants.RS_NORMAL, "xyz"), (constants.RS_NORMAL, 1024),
276 (constants.RS_NORMAL, True), (constants.RS_NORMAL, 4096),
277 (constants.RS_NORMAL, [{}, {}])],
280 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True),
282 "Name Size Active Memory SomeList",
283 "nodeA 128 N 1.4T []",
284 "other 512 Y 16M [1, 2, 3]",
285 "xyz 1024 Y 4.0G [{}, {}]",
288 def testTimestampAndUnit(self):
290 objects.QueryFieldDefinition(name="name", title="Name",
291 kind=constants.QFT_TEXT),
292 objects.QueryFieldDefinition(name="size", title="Size",
293 kind=constants.QFT_UNIT),
294 objects.QueryFieldDefinition(name="mtime", title="ModTime",
295 kind=constants.QFT_TIMESTAMP),
298 response = objects.QueryResponse(fields=fields, data=[
299 [(constants.RS_NORMAL, "a"), (constants.RS_NORMAL, 1024),
300 (constants.RS_NORMAL, 0)],
301 [(constants.RS_NORMAL, "b"), (constants.RS_NORMAL, 144996),
302 (constants.RS_NORMAL, 1291746295)],
305 self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True),
308 "a 1024 %s" % utils.FormatTime(0),
309 "b 144996 %s" % utils.FormatTime(1291746295),
312 def testOverride(self):
314 objects.QueryFieldDefinition(name="name", title="Name",
315 kind=constants.QFT_TEXT),
316 objects.QueryFieldDefinition(name="cust", title="Custom",
317 kind=constants.QFT_OTHER),
318 objects.QueryFieldDefinition(name="xt", title="XTime",
319 kind=constants.QFT_TIMESTAMP),
322 response = objects.QueryResponse(fields=fields, data=[
323 [(constants.RS_NORMAL, "x"), (constants.RS_NORMAL, ["a", "b", "c"]),
324 (constants.RS_NORMAL, 1234)],
325 [(constants.RS_NORMAL, "y"), (constants.RS_NORMAL, range(10)),
326 (constants.RS_NORMAL, 1291746295)],
330 "cust": (utils.CommaJoin, False),
334 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True,
335 format_override=override),
339 "y 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
342 def testSeparator(self):
344 objects.QueryFieldDefinition(name="name", title="Name",
345 kind=constants.QFT_TEXT),
346 objects.QueryFieldDefinition(name="count", title="Count",
347 kind=constants.QFT_NUMBER),
348 objects.QueryFieldDefinition(name="desc", title="Description",
349 kind=constants.QFT_TEXT),
352 response = objects.QueryResponse(fields=fields, data=[
353 [(constants.RS_NORMAL, "instance1.example.com"),
354 (constants.RS_NORMAL, 21125), (constants.RS_NORMAL, "Hello World!")],
355 [(constants.RS_NORMAL, "mail.other.net"),
356 (constants.RS_NORMAL, -9000), (constants.RS_NORMAL, "a,b,c")],
359 for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]:
360 for header in [None, "Name%sCount%sDescription" % (sep, sep)]:
365 "instance1.example.com%s21125%sHello World!" % (sep, sep),
366 "mail.other.net%s-9000%sa,b,c" % (sep, sep),
369 self.assertEqual(cli.FormatQueryResult(response, separator=sep,
370 header=bool(header)),
371 (cli.QR_NORMAL, exp))
373 def testStatusWithUnknown(self):
375 objects.QueryFieldDefinition(name="id", title="ID",
376 kind=constants.QFT_NUMBER),
377 objects.QueryFieldDefinition(name="unk", title="unk",
378 kind=constants.QFT_UNKNOWN),
379 objects.QueryFieldDefinition(name="unavail", title="Unavail",
380 kind=constants.QFT_BOOL),
381 objects.QueryFieldDefinition(name="nodata", title="NoData",
382 kind=constants.QFT_TEXT),
383 objects.QueryFieldDefinition(name="offline", title="OffLine",
384 kind=constants.QFT_TEXT),
387 response = objects.QueryResponse(fields=fields, data=[
388 [(constants.RS_NORMAL, 1), (constants.RS_UNKNOWN, None),
389 (constants.RS_NORMAL, False), (constants.RS_NORMAL, ""),
390 (constants.RS_OFFLINE, None)],
391 [(constants.RS_NORMAL, 2), (constants.RS_UNKNOWN, None),
392 (constants.RS_NODATA, None), (constants.RS_NORMAL, "x"),
393 (constants.RS_OFFLINE, None)],
394 [(constants.RS_NORMAL, 3), (constants.RS_UNKNOWN, None),
395 (constants.RS_NORMAL, False), (constants.RS_UNAVAIL, None),
396 (constants.RS_OFFLINE, None)],
399 self.assertEqual(cli.FormatQueryResult(response, header=True,
400 separator="|", verbose=True),
402 "ID|unk|Unavail|NoData|OffLine",
403 "1|(unknown)|N||(offline)",
404 "2|(unknown)|(nodata)|x|(offline)",
405 "3|(unknown)|N|(unavail)|(offline)",
407 self.assertEqual(cli.FormatQueryResult(response, header=True,
408 separator="|", verbose=False),
410 "ID|unk|Unavail|NoData|OffLine",
416 def testNoData(self):
418 objects.QueryFieldDefinition(name="id", title="ID",
419 kind=constants.QFT_NUMBER),
420 objects.QueryFieldDefinition(name="name", title="Name",
421 kind=constants.QFT_TEXT),
424 response = objects.QueryResponse(fields=fields, data=[])
426 self.assertEqual(cli.FormatQueryResult(response, header=True),
427 (cli.QR_NORMAL, ["ID Name"]))
429 def testNoDataWithUnknown(self):
431 objects.QueryFieldDefinition(name="id", title="ID",
432 kind=constants.QFT_NUMBER),
433 objects.QueryFieldDefinition(name="unk", title="unk",
434 kind=constants.QFT_UNKNOWN),
437 response = objects.QueryResponse(fields=fields, data=[])
439 self.assertEqual(cli.FormatQueryResult(response, header=False),
440 (cli.QR_UNKNOWN, []))
442 def testStatus(self):
444 objects.QueryFieldDefinition(name="id", title="ID",
445 kind=constants.QFT_NUMBER),
446 objects.QueryFieldDefinition(name="unavail", title="Unavail",
447 kind=constants.QFT_BOOL),
448 objects.QueryFieldDefinition(name="nodata", title="NoData",
449 kind=constants.QFT_TEXT),
450 objects.QueryFieldDefinition(name="offline", title="OffLine",
451 kind=constants.QFT_TEXT),
454 response = objects.QueryResponse(fields=fields, data=[
455 [(constants.RS_NORMAL, 1), (constants.RS_NORMAL, False),
456 (constants.RS_NORMAL, ""), (constants.RS_OFFLINE, None)],
457 [(constants.RS_NORMAL, 2), (constants.RS_NODATA, None),
458 (constants.RS_NORMAL, "x"), (constants.RS_NORMAL, "abc")],
459 [(constants.RS_NORMAL, 3), (constants.RS_NORMAL, False),
460 (constants.RS_UNAVAIL, None), (constants.RS_OFFLINE, None)],
463 self.assertEqual(cli.FormatQueryResult(response, header=False,
464 separator="|", verbose=True),
465 (cli.QR_INCOMPLETE, [
468 "3|N|(unavail)|(offline)",
470 self.assertEqual(cli.FormatQueryResult(response, header=False,
471 separator="|", verbose=False),
472 (cli.QR_INCOMPLETE, [
478 def testInvalidFieldType(self):
480 objects.QueryFieldDefinition(name="x", title="x",
481 kind="#some#other#type"),
484 response = objects.QueryResponse(fields=fields, data=[])
486 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
488 def testInvalidFieldStatus(self):
490 objects.QueryFieldDefinition(name="x", title="x",
491 kind=constants.QFT_TEXT),
494 response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
495 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
497 response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
498 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
500 def testEmptyFieldTitle(self):
502 objects.QueryFieldDefinition(name="x", title="",
503 kind=constants.QFT_TEXT),
506 response = objects.QueryResponse(fields=fields, data=[])
507 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
510 class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
511 def __init__(self, tc, job_id):
516 self._expect_notchanged = False
517 self._expect_log = []
519 def CheckEmpty(self):
520 self.tc.assertFalse(self._wfjcr)
521 self.tc.assertFalse(self._jobstatus)
522 self.tc.assertFalse(self._expect_notchanged)
523 self.tc.assertFalse(self._expect_log)
525 def AddWfjcResult(self, *args):
526 self._wfjcr.append(args)
528 def AddQueryJobsResult(self, *args):
529 self._jobstatus.append(args)
531 def WaitForJobChangeOnce(self, job_id, fields,
532 prev_job_info, prev_log_serial):
533 self.tc.assertEqual(job_id, self.job_id)
534 self.tc.assertEqualValues(fields, ["status"])
535 self.tc.assertFalse(self._expect_notchanged)
536 self.tc.assertFalse(self._expect_log)
538 (exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0)
539 self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
540 self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
542 if result == constants.JOB_NOTCHANGED:
543 self._expect_notchanged = True
545 (_, logmsgs) = result
547 self._expect_log.extend(logmsgs)
551 def QueryJobs(self, job_ids, fields):
552 self.tc.assertEqual(job_ids, [self.job_id])
553 self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"])
554 self.tc.assertFalse(self._expect_notchanged)
555 self.tc.assertFalse(self._expect_log)
557 result = self._jobstatus.pop(0)
558 self.tc.assertEqual(len(fields), len(result))
561 def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
562 self.tc.assertEqual(job_id, self.job_id)
563 self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
564 self._expect_log.pop(0))
566 def ReportNotChanged(self, job_id, status):
567 self.tc.assertEqual(job_id, self.job_id)
568 self.tc.assert_(self._expect_notchanged)
569 self._expect_notchanged = False
572 class TestGenericPollJob(testutils.GanetiTestCase):
573 def testSuccessWithLog(self):
575 cbs = _MockJobPollCb(self, job_id)
577 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
579 cbs.AddWfjcResult(None, None,
580 ((constants.JOB_STATUS_QUEUED, ), None))
582 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
583 constants.JOB_NOTCHANGED)
585 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
586 ((constants.JOB_STATUS_RUNNING, ),
587 [(1, utils.SplitTime(1273491611.0),
588 constants.ELOG_MESSAGE, "Step 1"),
589 (2, utils.SplitTime(1273491615.9),
590 constants.ELOG_MESSAGE, "Step 2"),
591 (3, utils.SplitTime(1273491625.02),
592 constants.ELOG_MESSAGE, "Step 3"),
593 (4, utils.SplitTime(1273491635.05),
594 constants.ELOG_MESSAGE, "Step 4"),
595 (37, utils.SplitTime(1273491645.0),
596 constants.ELOG_MESSAGE, "Step 5"),
597 (203, utils.SplitTime(127349155.0),
598 constants.ELOG_MESSAGE, "Step 6")]))
600 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
601 ((constants.JOB_STATUS_RUNNING, ),
602 [(300, utils.SplitTime(1273491711.01),
603 constants.ELOG_MESSAGE, "Step X"),
604 (302, utils.SplitTime(1273491815.8),
605 constants.ELOG_MESSAGE, "Step Y"),
606 (303, utils.SplitTime(1273491925.32),
607 constants.ELOG_MESSAGE, "Step Z")]))
609 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
610 ((constants.JOB_STATUS_SUCCESS, ), None))
612 cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
613 [constants.OP_STATUS_SUCCESS,
614 constants.OP_STATUS_SUCCESS],
615 ["Hello World", "Foo man bar"])
617 self.assertEqual(["Hello World", "Foo man bar"],
618 cli.GenericPollJob(job_id, cbs, cbs))
621 def testJobLost(self):
624 cbs = _MockJobPollCb(self, job_id)
625 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
626 cbs.AddWfjcResult(None, None, None)
627 self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
633 cbs = _MockJobPollCb(self, job_id)
634 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
635 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
636 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
637 [constants.OP_STATUS_SUCCESS,
638 constants.OP_STATUS_ERROR],
639 ["Hello World", "Error code 123"])
640 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
643 def testError2(self):
646 cbs = _MockJobPollCb(self, job_id)
647 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
648 encexc = errors.EncodeException(errors.LockError("problem"))
649 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
650 [constants.OP_STATUS_ERROR], [encexc])
651 self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
654 def testWeirdError(self):
657 cbs = _MockJobPollCb(self, job_id)
658 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
659 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
660 [constants.OP_STATUS_RUNNING,
661 constants.OP_STATUS_RUNNING],
663 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
666 def testCancel(self):
669 cbs = _MockJobPollCb(self, job_id)
670 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
671 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None))
672 cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING,
673 [constants.OP_STATUS_CANCELING,
674 constants.OP_STATUS_CANCELING],
676 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
680 class TestFormatLogMessage(unittest.TestCase):
682 self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
685 self.assertRaises(TypeError, cli.FormatLogMessage,
686 constants.ELOG_MESSAGE, [1, 2, 3])
688 self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
691 class TestParseFields(unittest.TestCase):
693 self.assertEqual(cli.ParseFields(None, []), [])
694 self.assertEqual(cli.ParseFields("name,foo,hello", []),
695 ["name", "foo", "hello"])
696 self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]),
697 ["def", "ault", "fields", "here"])
698 self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]),
700 self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
701 ["def", "ault", "name", "foo"])
704 class TestConstants(unittest.TestCase):
705 def testPriority(self):
706 self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()),
707 set(constants.OP_PRIO_SUBMIT_VALID))
708 self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES),
709 sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True))
712 class TestParseNicOption(unittest.TestCase):
714 self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]),
715 [{ "link": "eth0", }])
716 self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]),
717 [{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }])
719 def testErrors(self):
720 for i in [None, "", "abc", "zero", "Hello World", "\0", []]:
721 self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
722 [(i, { "link": "eth0", })])
723 self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
726 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
727 [(0, { True: False, })])
729 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
730 [(3, { "mode": [], })])
733 class TestFormatResultError(unittest.TestCase):
734 def testNormal(self):
735 for verbose in [False, True]:
736 self.assertRaises(AssertionError, cli.FormatResultError,
737 constants.RS_NORMAL, verbose)
739 def testUnknown(self):
740 for verbose in [False, True]:
741 self.assertRaises(NotImplementedError, cli.FormatResultError,
742 "#some!other!status#", verbose)
745 for status in constants.RS_ALL:
746 if status == constants.RS_NORMAL:
749 self.assertNotEqual(cli.FormatResultError(status, False),
750 cli.FormatResultError(status, True))
752 result = cli.FormatResultError(status, True)
753 self.assertTrue(result.startswith("("))
754 self.assertTrue(result.endswith(")"))
757 class TestGetOnlineNodes(unittest.TestCase):
762 def AddQueryResult(self, *args):
763 self._query.append(args)
765 def CountPending(self):
766 return len(self._query)
768 def Query(self, res, fields, filter_):
769 if res != constants.QR_NODE:
770 raise Exception("Querying wrong resource")
772 (exp_fields, check_filter, result) = self._query.pop(0)
774 if exp_fields != fields:
775 raise Exception("Expected fields %s, got %s" % (exp_fields, fields))
777 if not (filter_ is None or check_filter(filter_)):
778 raise Exception("Filter doesn't match expectations")
780 return objects.QueryResponse(fields=None, data=result)
783 cl = self._FakeClient()
785 cl.AddQueryResult(["name", "offline", "sip"], None, [])
786 self.assertEqual(cli.GetOnlineNodes(None, cl=cl), [])
787 self.assertEqual(cl.CountPending(), 0)
789 def testNoSpecialFilter(self):
790 cl = self._FakeClient()
792 cl.AddQueryResult(["name", "offline", "sip"], None, [
793 [(constants.RS_NORMAL, "master.example.com"),
794 (constants.RS_NORMAL, False),
795 (constants.RS_NORMAL, "192.0.2.1")],
796 [(constants.RS_NORMAL, "node2.example.com"),
797 (constants.RS_NORMAL, False),
798 (constants.RS_NORMAL, "192.0.2.2")],
800 self.assertEqual(cli.GetOnlineNodes(None, cl=cl),
801 ["master.example.com", "node2.example.com"])
802 self.assertEqual(cl.CountPending(), 0)
804 def testNoMaster(self):
805 cl = self._FakeClient()
807 def _CheckFilter(filter_):
808 self.assertEqual(filter_, [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
811 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
812 [(constants.RS_NORMAL, "node2.example.com"),
813 (constants.RS_NORMAL, False),
814 (constants.RS_NORMAL, "192.0.2.2")],
816 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, filter_master=True),
817 ["node2.example.com"])
818 self.assertEqual(cl.CountPending(), 0)
820 def testSecondaryIpAddress(self):
821 cl = self._FakeClient()
823 cl.AddQueryResult(["name", "offline", "sip"], None, [
824 [(constants.RS_NORMAL, "master.example.com"),
825 (constants.RS_NORMAL, False),
826 (constants.RS_NORMAL, "192.0.2.1")],
827 [(constants.RS_NORMAL, "node2.example.com"),
828 (constants.RS_NORMAL, False),
829 (constants.RS_NORMAL, "192.0.2.2")],
831 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, secondary_ips=True),
832 ["192.0.2.1", "192.0.2.2"])
833 self.assertEqual(cl.CountPending(), 0)
835 def testNoMasterFilterNodeName(self):
836 cl = self._FakeClient()
838 def _CheckFilter(filter_):
839 self.assertEqual(filter_,
841 [qlang.OP_OR] + [[qlang.OP_EQUAL, "name", name]
842 for name in ["node2", "node3"]],
843 [qlang.OP_NOT, [qlang.OP_TRUE, "master"]]])
846 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
847 [(constants.RS_NORMAL, "node2.example.com"),
848 (constants.RS_NORMAL, False),
849 (constants.RS_NORMAL, "192.0.2.12")],
850 [(constants.RS_NORMAL, "node3.example.com"),
851 (constants.RS_NORMAL, False),
852 (constants.RS_NORMAL, "192.0.2.13")],
854 self.assertEqual(cli.GetOnlineNodes(["node2", "node3"], cl=cl,
855 secondary_ips=True, filter_master=True),
856 ["192.0.2.12", "192.0.2.13"])
857 self.assertEqual(cl.CountPending(), 0)
859 def testOfflineNodes(self):
860 cl = self._FakeClient()
862 cl.AddQueryResult(["name", "offline", "sip"], None, [
863 [(constants.RS_NORMAL, "master.example.com"),
864 (constants.RS_NORMAL, False),
865 (constants.RS_NORMAL, "192.0.2.1")],
866 [(constants.RS_NORMAL, "node2.example.com"),
867 (constants.RS_NORMAL, True),
868 (constants.RS_NORMAL, "192.0.2.2")],
869 [(constants.RS_NORMAL, "node3.example.com"),
870 (constants.RS_NORMAL, True),
871 (constants.RS_NORMAL, "192.0.2.3")],
873 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nowarn=True),
874 ["master.example.com"])
875 self.assertEqual(cl.CountPending(), 0)
877 def testNodeGroup(self):
878 cl = self._FakeClient()
880 def _CheckFilter(filter_):
881 self.assertEqual(filter_,
882 [qlang.OP_OR, [qlang.OP_EQUAL, "group", "foobar"],
883 [qlang.OP_EQUAL, "group.uuid", "foobar"]])
886 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
887 [(constants.RS_NORMAL, "master.example.com"),
888 (constants.RS_NORMAL, False),
889 (constants.RS_NORMAL, "192.0.2.1")],
890 [(constants.RS_NORMAL, "node3.example.com"),
891 (constants.RS_NORMAL, False),
892 (constants.RS_NORMAL, "192.0.2.3")],
894 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nodegroup="foobar"),
895 ["master.example.com", "node3.example.com"])
896 self.assertEqual(cl.CountPending(), 0)
899 if __name__ == '__main__':
900 testutils.GanetiTestProgram()