4 # Copyright (C) 2008, 2011, 2012, 2013 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"""
29 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}
74 RESULT_NOPREFIX = {"a": "b", "c": {}, "no_d": {}, "-e": {}}
76 def testSplitKeyVal(self):
78 self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA, True),
81 def testDuplicateParam(self):
82 """Test duplicate parameters"""
83 for data in ("a=1,a=2", "a,no_a"):
84 self.failUnlessRaises(ParameterError, cli._SplitKeyVal,
87 def testEmptyData(self):
88 """Test how we handle splitting an empty string"""
89 self.failUnlessEqual(cli._SplitKeyVal("option", "", True), {})
92 class TestIdentKeyVal(unittest.TestCase):
93 """Testing case for cli.check_ident_key_val"""
95 def testIdentKeyVal(self):
96 """Test identkeyval"""
98 return cli.check_ident_key_val("option", "opt", value)
100 self.assertEqual(cikv("foo:bar"), ("foo", {"bar": True}))
101 self.assertEqual(cikv("foo:bar=baz"), ("foo", {"bar": "baz"}))
102 self.assertEqual(cikv("bar:b=c,c=a"), ("bar", {"b": "c", "c": "a"}))
103 self.assertEqual(cikv("no_bar"), ("bar", False))
104 self.assertRaises(ParameterError, cikv, "no_bar:foo")
105 self.assertRaises(ParameterError, cikv, "no_bar:foo=baz")
106 self.assertRaises(ParameterError, cikv, "bar:foo=baz,foo=baz")
107 self.assertEqual(cikv("-foo"), ("foo", None))
108 self.assertRaises(ParameterError, cikv, "-foo:a=c")
110 # Check negative numbers
111 self.assertEqual(cikv("-1:remove"), ("-1", {
114 self.assertEqual(cikv("-29447:add,size=4G"), ("-29447", {
118 for i in ["-:", "-"]:
119 self.assertEqual(cikv(i), ("", None))
123 return cli._SplitIdentKeyVal("opt", value, False)
125 def testIdentKeyValNoPrefix(self):
126 """Test identkeyval without prefixes"""
129 ("foo:no_bar", None),
130 ("foo:bar=baz,bar=baz", None),
134 ("foo", {"bar": "baz"})),
135 ("no_foo:-1=baz,no_op=3",
136 ("no_foo", {"-1": "baz", "no_op": "3"})),
138 for (arg, res) in test_cases:
140 self.assertRaises(ParameterError, self._csikv, arg)
142 self.assertEqual(self._csikv(arg), res)
145 class TestMultilistIdentKeyVal(unittest.TestCase):
146 """Test for cli.check_multilist_ident_key_val()"""
150 return cli.check_multilist_ident_key_val("option", "opt", value)
152 def testListIdentKeyVal(self):
160 {"foo": {"bar": "baz"}}
162 ("foo:bar=baz/foo:bat=bad",
164 ("foo:abc=42/bar:def=11", [
165 {"foo": {"abc": "42"},
166 "bar": {"def": "11"}}
168 ("foo:abc=42/bar:def=11,ghi=07", [
169 {"foo": {"abc": "42"},
170 "bar": {"def": "11", "ghi": "07"}}
172 ("foo:abc=42/bar:def=11//",
174 ("foo:abc=42/bar:def=11,ghi=07//foobar", [
175 {"foo": {"abc": "42"},
176 "bar": {"def": "11", "ghi": "07"}},
179 ("foo:abc=42/bar:def=11,ghi=07//foobar:xyz=88", [
180 {"foo": {"abc": "42"},
181 "bar": {"def": "11", "ghi": "07"}},
182 {"foobar": {"xyz": "88"}}
184 ("foo:abc=42/bar:def=11,ghi=07//foobar:xyz=88/foo:uvw=314", [
185 {"foo": {"abc": "42"},
186 "bar": {"def": "11", "ghi": "07"}},
187 {"foobar": {"xyz": "88"},
188 "foo": {"uvw": "314"}}
191 for (arg, res) in test_cases:
193 self.assertRaises(ParameterError, self._cmikv, arg)
195 self.assertEqual(res, self._cmikv(arg))
198 class TestToStream(unittest.TestCase):
199 """Test the ToStream functions"""
209 cli._ToStream(buf, data)
210 self.failUnlessEqual(buf.getvalue(), data + "\n")
212 def testParams(self):
214 cli._ToStream(buf, "foo %s", 1)
215 self.failUnlessEqual(buf.getvalue(), "foo 1\n")
217 cli._ToStream(buf, "foo %s", (15,16))
218 self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n")
220 cli._ToStream(buf, "foo %s %s", "a", "b")
221 self.failUnlessEqual(buf.getvalue(), "foo a b\n")
224 class TestGenerateTable(unittest.TestCase):
225 HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
227 FIELDS1 = ["f1", "f2"]
234 def _test(self, headers, fields, separator, data,
235 numfields, unitfields, units, expected):
236 table = cli.GenerateTable(headers, fields, separator, data,
237 numfields=numfields, unitfields=unitfields,
239 self.assertEqual(table, expected)
248 self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
249 None, None, "m", exp)
251 def testNoFields(self):
252 self._test(self.HEADERS, [], None, [[], []],
253 None, None, "m", ["", "", ""])
254 self._test(None, [], None, [[], []],
255 None, None, "m", ["", ""])
257 def testSeparator(self):
258 for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
260 "Field1%sField2" % sep,
265 self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
266 None, None, "m", exp)
268 def testNoHeader(self):
274 self._test(None, self.FIELDS1, None, self.DATA1,
275 None, None, "m", exp)
277 def testUnknownField(self):
287 self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
288 None, None, "m", exp)
290 def testNumfields(self):
291 fields = ["f1", "f2", "f3"]
298 "Field1 Field2 Field3",
303 self._test(self.HEADERS, fields, None, data,
304 ["f2", "f3"], None, "m", exp)
306 def testUnitfields(self):
308 "Field1 Field2 Field3",
315 "Field1:Field2:Field3",
321 for sep, expected in [(None, expnosep), (":", expsep)]:
322 fields = ["f1", "f2", "f3"]
328 self._test(self.HEADERS, fields, sep, data,
329 ["f2", "f3"], ["f3"], "h", expected)
331 def testUnusual(self):
341 self._test(self.HEADERS, ["f1", "f2"], None, data,
342 None, None, "m", exp)
345 class TestFormatQueryResult(unittest.TestCase):
348 objects.QueryFieldDefinition(name="name", title="Name",
349 kind=constants.QFT_TEXT),
350 objects.QueryFieldDefinition(name="size", title="Size",
351 kind=constants.QFT_NUMBER),
352 objects.QueryFieldDefinition(name="act", title="Active",
353 kind=constants.QFT_BOOL),
354 objects.QueryFieldDefinition(name="mem", title="Memory",
355 kind=constants.QFT_UNIT),
356 objects.QueryFieldDefinition(name="other", title="SomeList",
357 kind=constants.QFT_OTHER),
360 response = objects.QueryResponse(fields=fields, data=[
361 [(constants.RS_NORMAL, "nodeA"), (constants.RS_NORMAL, 128),
362 (constants.RS_NORMAL, False), (constants.RS_NORMAL, 1468006),
363 (constants.RS_NORMAL, [])],
364 [(constants.RS_NORMAL, "other"), (constants.RS_NORMAL, 512),
365 (constants.RS_NORMAL, True), (constants.RS_NORMAL, 16),
366 (constants.RS_NORMAL, [1, 2, 3])],
367 [(constants.RS_NORMAL, "xyz"), (constants.RS_NORMAL, 1024),
368 (constants.RS_NORMAL, True), (constants.RS_NORMAL, 4096),
369 (constants.RS_NORMAL, [{}, {}])],
372 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True),
374 "Name Size Active Memory SomeList",
375 "nodeA 128 N 1.4T []",
376 "other 512 Y 16M [1, 2, 3]",
377 "xyz 1024 Y 4.0G [{}, {}]",
380 def testTimestampAndUnit(self):
382 objects.QueryFieldDefinition(name="name", title="Name",
383 kind=constants.QFT_TEXT),
384 objects.QueryFieldDefinition(name="size", title="Size",
385 kind=constants.QFT_UNIT),
386 objects.QueryFieldDefinition(name="mtime", title="ModTime",
387 kind=constants.QFT_TIMESTAMP),
390 response = objects.QueryResponse(fields=fields, data=[
391 [(constants.RS_NORMAL, "a"), (constants.RS_NORMAL, 1024),
392 (constants.RS_NORMAL, 0)],
393 [(constants.RS_NORMAL, "b"), (constants.RS_NORMAL, 144996),
394 (constants.RS_NORMAL, 1291746295)],
397 self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True),
400 "a 1024 %s" % utils.FormatTime(0),
401 "b 144996 %s" % utils.FormatTime(1291746295),
404 def testOverride(self):
406 objects.QueryFieldDefinition(name="name", title="Name",
407 kind=constants.QFT_TEXT),
408 objects.QueryFieldDefinition(name="cust", title="Custom",
409 kind=constants.QFT_OTHER),
410 objects.QueryFieldDefinition(name="xt", title="XTime",
411 kind=constants.QFT_TIMESTAMP),
414 response = objects.QueryResponse(fields=fields, data=[
415 [(constants.RS_NORMAL, "x"), (constants.RS_NORMAL, ["a", "b", "c"]),
416 (constants.RS_NORMAL, 1234)],
417 [(constants.RS_NORMAL, "y"), (constants.RS_NORMAL, range(10)),
418 (constants.RS_NORMAL, 1291746295)],
422 "cust": (utils.CommaJoin, False),
426 self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True,
427 format_override=override),
431 "y 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
434 def testSeparator(self):
436 objects.QueryFieldDefinition(name="name", title="Name",
437 kind=constants.QFT_TEXT),
438 objects.QueryFieldDefinition(name="count", title="Count",
439 kind=constants.QFT_NUMBER),
440 objects.QueryFieldDefinition(name="desc", title="Description",
441 kind=constants.QFT_TEXT),
444 response = objects.QueryResponse(fields=fields, data=[
445 [(constants.RS_NORMAL, "instance1.example.com"),
446 (constants.RS_NORMAL, 21125), (constants.RS_NORMAL, "Hello World!")],
447 [(constants.RS_NORMAL, "mail.other.net"),
448 (constants.RS_NORMAL, -9000), (constants.RS_NORMAL, "a,b,c")],
451 for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]:
452 for header in [None, "Name%sCount%sDescription" % (sep, sep)]:
457 "instance1.example.com%s21125%sHello World!" % (sep, sep),
458 "mail.other.net%s-9000%sa,b,c" % (sep, sep),
461 self.assertEqual(cli.FormatQueryResult(response, separator=sep,
462 header=bool(header)),
463 (cli.QR_NORMAL, exp))
465 def testStatusWithUnknown(self):
467 objects.QueryFieldDefinition(name="id", title="ID",
468 kind=constants.QFT_NUMBER),
469 objects.QueryFieldDefinition(name="unk", title="unk",
470 kind=constants.QFT_UNKNOWN),
471 objects.QueryFieldDefinition(name="unavail", title="Unavail",
472 kind=constants.QFT_BOOL),
473 objects.QueryFieldDefinition(name="nodata", title="NoData",
474 kind=constants.QFT_TEXT),
475 objects.QueryFieldDefinition(name="offline", title="OffLine",
476 kind=constants.QFT_TEXT),
479 response = objects.QueryResponse(fields=fields, data=[
480 [(constants.RS_NORMAL, 1), (constants.RS_UNKNOWN, None),
481 (constants.RS_NORMAL, False), (constants.RS_NORMAL, ""),
482 (constants.RS_OFFLINE, None)],
483 [(constants.RS_NORMAL, 2), (constants.RS_UNKNOWN, None),
484 (constants.RS_NODATA, None), (constants.RS_NORMAL, "x"),
485 (constants.RS_OFFLINE, None)],
486 [(constants.RS_NORMAL, 3), (constants.RS_UNKNOWN, None),
487 (constants.RS_NORMAL, False), (constants.RS_UNAVAIL, None),
488 (constants.RS_OFFLINE, None)],
491 self.assertEqual(cli.FormatQueryResult(response, header=True,
492 separator="|", verbose=True),
494 "ID|unk|Unavail|NoData|OffLine",
495 "1|(unknown)|N||(offline)",
496 "2|(unknown)|(nodata)|x|(offline)",
497 "3|(unknown)|N|(unavail)|(offline)",
499 self.assertEqual(cli.FormatQueryResult(response, header=True,
500 separator="|", verbose=False),
502 "ID|unk|Unavail|NoData|OffLine",
508 def testNoData(self):
510 objects.QueryFieldDefinition(name="id", title="ID",
511 kind=constants.QFT_NUMBER),
512 objects.QueryFieldDefinition(name="name", title="Name",
513 kind=constants.QFT_TEXT),
516 response = objects.QueryResponse(fields=fields, data=[])
518 self.assertEqual(cli.FormatQueryResult(response, header=True),
519 (cli.QR_NORMAL, ["ID Name"]))
521 def testNoDataWithUnknown(self):
523 objects.QueryFieldDefinition(name="id", title="ID",
524 kind=constants.QFT_NUMBER),
525 objects.QueryFieldDefinition(name="unk", title="unk",
526 kind=constants.QFT_UNKNOWN),
529 response = objects.QueryResponse(fields=fields, data=[])
531 self.assertEqual(cli.FormatQueryResult(response, header=False),
532 (cli.QR_UNKNOWN, []))
534 def testStatus(self):
536 objects.QueryFieldDefinition(name="id", title="ID",
537 kind=constants.QFT_NUMBER),
538 objects.QueryFieldDefinition(name="unavail", title="Unavail",
539 kind=constants.QFT_BOOL),
540 objects.QueryFieldDefinition(name="nodata", title="NoData",
541 kind=constants.QFT_TEXT),
542 objects.QueryFieldDefinition(name="offline", title="OffLine",
543 kind=constants.QFT_TEXT),
546 response = objects.QueryResponse(fields=fields, data=[
547 [(constants.RS_NORMAL, 1), (constants.RS_NORMAL, False),
548 (constants.RS_NORMAL, ""), (constants.RS_OFFLINE, None)],
549 [(constants.RS_NORMAL, 2), (constants.RS_NODATA, None),
550 (constants.RS_NORMAL, "x"), (constants.RS_NORMAL, "abc")],
551 [(constants.RS_NORMAL, 3), (constants.RS_NORMAL, False),
552 (constants.RS_UNAVAIL, None), (constants.RS_OFFLINE, None)],
555 self.assertEqual(cli.FormatQueryResult(response, header=False,
556 separator="|", verbose=True),
557 (cli.QR_INCOMPLETE, [
560 "3|N|(unavail)|(offline)",
562 self.assertEqual(cli.FormatQueryResult(response, header=False,
563 separator="|", verbose=False),
564 (cli.QR_INCOMPLETE, [
570 def testInvalidFieldType(self):
572 objects.QueryFieldDefinition(name="x", title="x",
573 kind="#some#other#type"),
576 response = objects.QueryResponse(fields=fields, data=[])
578 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
580 def testInvalidFieldStatus(self):
582 objects.QueryFieldDefinition(name="x", title="x",
583 kind=constants.QFT_TEXT),
586 response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
587 self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
589 response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
590 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
592 def testEmptyFieldTitle(self):
594 objects.QueryFieldDefinition(name="x", title="",
595 kind=constants.QFT_TEXT),
598 response = objects.QueryResponse(fields=fields, data=[])
599 self.assertRaises(AssertionError, cli.FormatQueryResult, response)
602 class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
603 def __init__(self, tc, job_id):
608 self._expect_notchanged = False
609 self._expect_log = []
611 def CheckEmpty(self):
612 self.tc.assertFalse(self._wfjcr)
613 self.tc.assertFalse(self._jobstatus)
614 self.tc.assertFalse(self._expect_notchanged)
615 self.tc.assertFalse(self._expect_log)
617 def AddWfjcResult(self, *args):
618 self._wfjcr.append(args)
620 def AddQueryJobsResult(self, *args):
621 self._jobstatus.append(args)
623 def WaitForJobChangeOnce(self, job_id, fields,
624 prev_job_info, prev_log_serial):
625 self.tc.assertEqual(job_id, self.job_id)
626 self.tc.assertEqualValues(fields, ["status"])
627 self.tc.assertFalse(self._expect_notchanged)
628 self.tc.assertFalse(self._expect_log)
630 (exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0)
631 self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
632 self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
634 if result == constants.JOB_NOTCHANGED:
635 self._expect_notchanged = True
637 (_, logmsgs) = result
639 self._expect_log.extend(logmsgs)
643 def QueryJobs(self, job_ids, fields):
644 self.tc.assertEqual(job_ids, [self.job_id])
645 self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"])
646 self.tc.assertFalse(self._expect_notchanged)
647 self.tc.assertFalse(self._expect_log)
649 result = self._jobstatus.pop(0)
650 self.tc.assertEqual(len(fields), len(result))
653 def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
654 self.tc.assertEqual(job_id, self.job_id)
655 self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
656 self._expect_log.pop(0))
658 def ReportNotChanged(self, job_id, status):
659 self.tc.assertEqual(job_id, self.job_id)
660 self.tc.assert_(self._expect_notchanged)
661 self._expect_notchanged = False
664 class TestGenericPollJob(testutils.GanetiTestCase):
665 def testSuccessWithLog(self):
667 cbs = _MockJobPollCb(self, job_id)
669 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
671 cbs.AddWfjcResult(None, None,
672 ((constants.JOB_STATUS_QUEUED, ), None))
674 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
675 constants.JOB_NOTCHANGED)
677 cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
678 ((constants.JOB_STATUS_RUNNING, ),
679 [(1, utils.SplitTime(1273491611.0),
680 constants.ELOG_MESSAGE, "Step 1"),
681 (2, utils.SplitTime(1273491615.9),
682 constants.ELOG_MESSAGE, "Step 2"),
683 (3, utils.SplitTime(1273491625.02),
684 constants.ELOG_MESSAGE, "Step 3"),
685 (4, utils.SplitTime(1273491635.05),
686 constants.ELOG_MESSAGE, "Step 4"),
687 (37, utils.SplitTime(1273491645.0),
688 constants.ELOG_MESSAGE, "Step 5"),
689 (203, utils.SplitTime(127349155.0),
690 constants.ELOG_MESSAGE, "Step 6")]))
692 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
693 ((constants.JOB_STATUS_RUNNING, ),
694 [(300, utils.SplitTime(1273491711.01),
695 constants.ELOG_MESSAGE, "Step X"),
696 (302, utils.SplitTime(1273491815.8),
697 constants.ELOG_MESSAGE, "Step Y"),
698 (303, utils.SplitTime(1273491925.32),
699 constants.ELOG_MESSAGE, "Step Z")]))
701 cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
702 ((constants.JOB_STATUS_SUCCESS, ), None))
704 cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
705 [constants.OP_STATUS_SUCCESS,
706 constants.OP_STATUS_SUCCESS],
707 ["Hello World", "Foo man bar"])
709 self.assertEqual(["Hello World", "Foo man bar"],
710 cli.GenericPollJob(job_id, cbs, cbs))
713 def testJobLost(self):
716 cbs = _MockJobPollCb(self, job_id)
717 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
718 cbs.AddWfjcResult(None, None, None)
719 self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
725 cbs = _MockJobPollCb(self, job_id)
726 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
727 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
728 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
729 [constants.OP_STATUS_SUCCESS,
730 constants.OP_STATUS_ERROR],
731 ["Hello World", "Error code 123"])
732 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
735 def testError2(self):
738 cbs = _MockJobPollCb(self, job_id)
739 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
740 encexc = errors.EncodeException(errors.LockError("problem"))
741 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
742 [constants.OP_STATUS_ERROR], [encexc])
743 self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
746 def testWeirdError(self):
749 cbs = _MockJobPollCb(self, job_id)
750 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
751 cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
752 [constants.OP_STATUS_RUNNING,
753 constants.OP_STATUS_RUNNING],
755 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
758 def testCancel(self):
761 cbs = _MockJobPollCb(self, job_id)
762 cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
763 cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None))
764 cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING,
765 [constants.OP_STATUS_CANCELING,
766 constants.OP_STATUS_CANCELING],
768 self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
772 class TestFormatLogMessage(unittest.TestCase):
774 self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
777 self.assertRaises(TypeError, cli.FormatLogMessage,
778 constants.ELOG_MESSAGE, [1, 2, 3])
780 self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
783 class TestParseFields(unittest.TestCase):
785 self.assertEqual(cli.ParseFields(None, []), [])
786 self.assertEqual(cli.ParseFields("name,foo,hello", []),
787 ["name", "foo", "hello"])
788 self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]),
789 ["def", "ault", "fields", "here"])
790 self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]),
792 self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
793 ["def", "ault", "name", "foo"])
796 class TestConstants(unittest.TestCase):
797 def testPriority(self):
798 self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()),
799 set(constants.OP_PRIO_SUBMIT_VALID))
800 self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES),
801 sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True))
804 class TestParseNicOption(unittest.TestCase):
806 self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]),
807 [{ "link": "eth0", }])
808 self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]),
809 [{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }])
811 def testErrors(self):
812 for i in [None, "", "abc", "zero", "Hello World", "\0", []]:
813 self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
814 [(i, { "link": "eth0", })])
815 self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
818 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
819 [(0, { True: False, })])
821 self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
822 [(3, { "mode": [], })])
825 class TestFormatResultError(unittest.TestCase):
826 def testNormal(self):
827 for verbose in [False, True]:
828 self.assertRaises(AssertionError, cli.FormatResultError,
829 constants.RS_NORMAL, verbose)
831 def testUnknown(self):
832 for verbose in [False, True]:
833 self.assertRaises(NotImplementedError, cli.FormatResultError,
834 "#some!other!status#", verbose)
837 for status in constants.RS_ALL:
838 if status == constants.RS_NORMAL:
841 self.assertNotEqual(cli.FormatResultError(status, False),
842 cli.FormatResultError(status, True))
844 result = cli.FormatResultError(status, True)
845 self.assertTrue(result.startswith("("))
846 self.assertTrue(result.endswith(")"))
849 class TestGetOnlineNodes(unittest.TestCase):
854 def AddQueryResult(self, *args):
855 self._query.append(args)
857 def CountPending(self):
858 return len(self._query)
860 def Query(self, res, fields, qfilter):
861 if res != constants.QR_NODE:
862 raise Exception("Querying wrong resource")
864 (exp_fields, check_filter, result) = self._query.pop(0)
866 if exp_fields != fields:
867 raise Exception("Expected fields %s, got %s" % (exp_fields, fields))
869 if not (qfilter is None or check_filter(qfilter)):
870 raise Exception("Filter doesn't match expectations")
872 return objects.QueryResponse(fields=None, data=result)
875 cl = self._FakeClient()
877 cl.AddQueryResult(["name", "offline", "sip"], None, [])
878 self.assertEqual(cli.GetOnlineNodes(None, cl=cl), [])
879 self.assertEqual(cl.CountPending(), 0)
881 def testNoSpecialFilter(self):
882 cl = self._FakeClient()
884 cl.AddQueryResult(["name", "offline", "sip"], None, [
885 [(constants.RS_NORMAL, "master.example.com"),
886 (constants.RS_NORMAL, False),
887 (constants.RS_NORMAL, "192.0.2.1")],
888 [(constants.RS_NORMAL, "node2.example.com"),
889 (constants.RS_NORMAL, False),
890 (constants.RS_NORMAL, "192.0.2.2")],
892 self.assertEqual(cli.GetOnlineNodes(None, cl=cl),
893 ["master.example.com", "node2.example.com"])
894 self.assertEqual(cl.CountPending(), 0)
896 def testNoMaster(self):
897 cl = self._FakeClient()
899 def _CheckFilter(qfilter):
900 self.assertEqual(qfilter, [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
903 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
904 [(constants.RS_NORMAL, "node2.example.com"),
905 (constants.RS_NORMAL, False),
906 (constants.RS_NORMAL, "192.0.2.2")],
908 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, filter_master=True),
909 ["node2.example.com"])
910 self.assertEqual(cl.CountPending(), 0)
912 def testSecondaryIpAddress(self):
913 cl = self._FakeClient()
915 cl.AddQueryResult(["name", "offline", "sip"], None, [
916 [(constants.RS_NORMAL, "master.example.com"),
917 (constants.RS_NORMAL, False),
918 (constants.RS_NORMAL, "192.0.2.1")],
919 [(constants.RS_NORMAL, "node2.example.com"),
920 (constants.RS_NORMAL, False),
921 (constants.RS_NORMAL, "192.0.2.2")],
923 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, secondary_ips=True),
924 ["192.0.2.1", "192.0.2.2"])
925 self.assertEqual(cl.CountPending(), 0)
927 def testNoMasterFilterNodeName(self):
928 cl = self._FakeClient()
930 def _CheckFilter(qfilter):
931 self.assertEqual(qfilter,
933 [qlang.OP_OR] + [[qlang.OP_EQUAL, "name", name]
934 for name in ["node2", "node3"]],
935 [qlang.OP_NOT, [qlang.OP_TRUE, "master"]]])
938 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
939 [(constants.RS_NORMAL, "node2.example.com"),
940 (constants.RS_NORMAL, False),
941 (constants.RS_NORMAL, "192.0.2.12")],
942 [(constants.RS_NORMAL, "node3.example.com"),
943 (constants.RS_NORMAL, False),
944 (constants.RS_NORMAL, "192.0.2.13")],
946 self.assertEqual(cli.GetOnlineNodes(["node2", "node3"], cl=cl,
947 secondary_ips=True, filter_master=True),
948 ["192.0.2.12", "192.0.2.13"])
949 self.assertEqual(cl.CountPending(), 0)
951 def testOfflineNodes(self):
952 cl = self._FakeClient()
954 cl.AddQueryResult(["name", "offline", "sip"], None, [
955 [(constants.RS_NORMAL, "master.example.com"),
956 (constants.RS_NORMAL, False),
957 (constants.RS_NORMAL, "192.0.2.1")],
958 [(constants.RS_NORMAL, "node2.example.com"),
959 (constants.RS_NORMAL, True),
960 (constants.RS_NORMAL, "192.0.2.2")],
961 [(constants.RS_NORMAL, "node3.example.com"),
962 (constants.RS_NORMAL, True),
963 (constants.RS_NORMAL, "192.0.2.3")],
965 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nowarn=True),
966 ["master.example.com"])
967 self.assertEqual(cl.CountPending(), 0)
969 def testNodeGroup(self):
970 cl = self._FakeClient()
972 def _CheckFilter(qfilter):
973 self.assertEqual(qfilter,
974 [qlang.OP_OR, [qlang.OP_EQUAL, "group", "foobar"],
975 [qlang.OP_EQUAL, "group.uuid", "foobar"]])
978 cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
979 [(constants.RS_NORMAL, "master.example.com"),
980 (constants.RS_NORMAL, False),
981 (constants.RS_NORMAL, "192.0.2.1")],
982 [(constants.RS_NORMAL, "node3.example.com"),
983 (constants.RS_NORMAL, False),
984 (constants.RS_NORMAL, "192.0.2.3")],
986 self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nodegroup="foobar"),
987 ["master.example.com", "node3.example.com"])
988 self.assertEqual(cl.CountPending(), 0)
991 class TestFormatTimestamp(unittest.TestCase):
993 self.assertEqual(cli.FormatTimestamp((0, 1)),
994 time.strftime("%F %T", time.localtime(0)) + ".000001")
995 self.assertEqual(cli.FormatTimestamp((1332944009, 17376)),
996 (time.strftime("%F %T", time.localtime(1332944009)) +
1000 for i in [0, [], {}, "", [1]]:
1001 self.assertEqual(cli.FormatTimestamp(i), "?")
1004 class TestFormatUsage(unittest.TestCase):
1006 binary = "gnt-unittest"
1009 (NotImplemented, NotImplemented, NotImplemented, NotImplemented,
1010 "description of A"),
1012 (NotImplemented, NotImplemented, NotImplemented, NotImplemented,
1013 "Hello World," * 10),
1015 (NotImplemented, NotImplemented, NotImplemented, NotImplemented,
1016 "Another description"),
1019 self.assertEqual(list(cli._FormatUsage(binary, commands)), [
1020 "Usage: gnt-unittest {command} [options...] [argument...]",
1021 "gnt-unittest <command> --help to see details, or man gnt-unittest",
1024 (" bbb - Hello World,Hello World,Hello World,Hello World,Hello"
1026 " World,Hello World,Hello World,Hello World,Hello World,",
1027 " cmdA - description of A",
1028 " longname - Another description",
1033 class TestParseArgs(unittest.TestCase):
1034 def testNoArguments(self):
1035 for argv in [[], ["gnt-unittest"]]:
1037 cli._ParseArgs("gnt-unittest", argv, {}, {}, set())
1038 except cli._ShowUsage, err:
1039 self.assertTrue(err.exit_error)
1041 self.fail("Did not raise exception")
1043 def testVersion(self):
1044 for argv in [["test", "--version"], ["test", "--version", "somethingelse"]]:
1046 cli._ParseArgs("test", argv, {}, {}, set())
1047 except cli._ShowVersion:
1050 self.fail("Did not raise exception")
1053 for argv in [["test", "--help"], ["test", "--help", "somethingelse"]]:
1055 cli._ParseArgs("test", argv, {}, {}, set())
1056 except cli._ShowUsage, err:
1057 self.assertFalse(err.exit_error)
1059 self.fail("Did not raise exception")
1061 def testUnknownCommandOrAlias(self):
1062 for argv in [["test", "list"], ["test", "somethingelse", "--help"]]:
1064 cli._ParseArgs("test", argv, {}, {}, set())
1065 except cli._ShowUsage, err:
1066 self.assertTrue(err.exit_error)
1068 self.fail("Did not raise exception")
1070 def testInvalidAliasList(self):
1072 "list": NotImplemented,
1073 "foo": NotImplemented,
1076 "list": NotImplemented,
1077 "foo": NotImplemented,
1079 assert sorted(cmd.keys()) == sorted(aliases.keys())
1080 self.assertRaises(AssertionError, cli._ParseArgs, "test",
1081 ["test", "list"], cmd, aliases, set())
1083 def testAliasForNonExistantCommand(self):
1086 "list": NotImplemented,
1088 self.assertRaises(errors.ProgrammerError, cli._ParseArgs, "test",
1089 ["test", "list"], cmd, aliases, set())
1092 class TestQftNames(unittest.TestCase):
1093 def testComplete(self):
1094 self.assertEqual(frozenset(cli._QFT_NAMES), constants.QFT_ALL)
1096 def testUnique(self):
1097 lcnames = map(lambda s: s.lower(), cli._QFT_NAMES.values())
1098 self.assertFalse(utils.FindDuplicates(lcnames))
1100 def testUppercase(self):
1101 for name in cli._QFT_NAMES.values():
1102 self.assertEqual(name[0], name[0].upper())
1105 class TestFieldDescValues(unittest.TestCase):
1106 def testKnownKind(self):
1107 fdef = objects.QueryFieldDefinition(name="aname",
1109 kind=constants.QFT_TEXT,
1111 self.assertEqual(cli._FieldDescValues(fdef),
1112 ["aname", "Text", "Atitle", "aaa doc aaa"])
1114 def testUnknownKind(self):
1117 self.assertFalse(kind in constants.QFT_ALL)
1118 self.assertFalse(kind in cli._QFT_NAMES)
1120 fdef = objects.QueryFieldDefinition(name="zname", title="Ztitle",
1121 kind=kind, doc="zzz doc zzz")
1122 self.assertEqual(cli._FieldDescValues(fdef),
1123 ["zname", kind, "Ztitle", "zzz doc zzz"])
1126 class TestSerializeGenericInfo(unittest.TestCase):
1127 """Test case for cli._SerializeGenericInfo"""
1128 def _RunTest(self, data, expected):
1130 cli._SerializeGenericInfo(buf, data, 0)
1131 self.assertEqual(buf.getvalue(), expected)
1133 def testSimple(self):
1138 (["1", "2", "3"], "- 1\n- 2\n- 3\n"),
1139 ([("z", "26")], "z: 26\n"),
1140 ({"z": "26"}, "z: 26\n"),
1141 ([("z", "26"), ("a", "1")], "z: 26\na: 1\n"),
1142 ({"z": "26", "a": "1"}, "a: 1\nz: 26\n"),
1144 for (data, expected) in test_samples:
1145 self._RunTest(data, expected)
1147 def testLists(self):
1153 adict_exp = ("- aa: 11\n"
1161 anobj_exp = ("- zz: 11\n"
1164 alist = ["aa", "cc", "bb"]
1165 alist_exp = ("- - aa\n"
1173 for (base_data, base_expected) in test_samples:
1174 for k in range(1, 4):
1175 data = k * [base_data]
1176 expected = k * base_expected
1177 self._RunTest(data, expected)
1179 def testDictionaries(self):
1181 ("aaa", ["x", "y"]),
1191 expected = ("aaa: \n"
1200 self._RunTest(data, expected)
1201 self._RunTest(dict(data), expected)
1204 class TestFormatPolicyInfo(unittest.TestCase):
1205 """Test case for cli.FormatPolicyInfo.
1207 These tests rely on cli._SerializeGenericInfo (tested elsewhere).
1211 # Policies are big, and we want to see the difference in case of an error
1214 def _RenameDictItem(self, parsed, old, new):
1215 self.assertTrue(old in parsed)
1216 self.assertTrue(new not in parsed)
1217 parsed[new] = parsed[old]
1220 def _TranslateParsedNames(self, parsed):
1221 for (pretty, raw) in [
1222 ("bounds specs", constants.ISPECS_MINMAX),
1223 ("allowed disk templates", constants.IPOLICY_DTS)
1225 self._RenameDictItem(parsed, pretty, raw)
1226 for minmax in parsed[constants.ISPECS_MINMAX]:
1228 keyparts = key.split("/", 1)
1229 if len(keyparts) > 1:
1230 self._RenameDictItem(minmax, key, keyparts[0])
1231 self.assertTrue(constants.IPOLICY_DTS in parsed)
1232 parsed[constants.IPOLICY_DTS] = yaml.load("[%s]" %
1233 parsed[constants.IPOLICY_DTS])
1236 def _PrintAndParsePolicy(custom, effective, iscluster):
1237 formatted = cli.FormatPolicyInfo(custom, effective, iscluster)
1239 cli._SerializeGenericInfo(buf, formatted, 0)
1240 return yaml.load(buf.getvalue())
1242 def _PrintAndCheckParsed(self, policy):
1243 parsed = self._PrintAndParsePolicy(policy, NotImplemented, True)
1244 self._TranslateParsedNames(parsed)
1245 self.assertEqual(parsed, policy)
1247 def _CompareClusterGroupItems(self, cluster, group, skip=None):
1248 if isinstance(group, dict):
1249 self.assertTrue(isinstance(cluster, dict))
1252 self.assertEqual(frozenset(cluster.keys()).difference(skip),
1253 frozenset(group.keys()))
1255 self._CompareClusterGroupItems(cluster[key], group[key])
1256 elif isinstance(group, list):
1257 self.assertTrue(isinstance(cluster, list))
1258 self.assertEqual(len(cluster), len(group))
1259 for (cval, gval) in zip(cluster, group):
1260 self._CompareClusterGroupItems(cval, gval)
1262 self.assertTrue(isinstance(group, basestring))
1263 self.assertEqual("default (%s)" % cluster, group)
1265 def _TestClusterVsGroup(self, policy):
1266 cluster = self._PrintAndParsePolicy(policy, NotImplemented, True)
1267 group = self._PrintAndParsePolicy({}, policy, False)
1268 self._CompareClusterGroupItems(cluster, group, ["std"])
1270 def testWithDefaults(self):
1271 self._PrintAndCheckParsed(constants.IPOLICY_DEFAULTS)
1272 self._TestClusterVsGroup(constants.IPOLICY_DEFAULTS)
1275 class TestCreateIPolicyFromOpts(unittest.TestCase):
1276 """Test case for cli.CreateIPolicyFromOpts."""
1278 # Policies are big, and we want to see the difference in case of an error
1281 def _RecursiveCheckMergedDicts(self, default_pol, diff_pol, merged_pol,
1282 merge_minmax=False):
1283 self.assertTrue(type(default_pol) is dict)
1284 self.assertTrue(type(diff_pol) is dict)
1285 self.assertTrue(type(merged_pol) is dict)
1286 self.assertEqual(frozenset(default_pol.keys()),
1287 frozenset(merged_pol.keys()))
1288 for (key, val) in merged_pol.items():
1290 if type(val) is dict:
1291 self._RecursiveCheckMergedDicts(default_pol[key], diff_pol[key], val)
1292 elif (merge_minmax and key == "minmax" and type(val) is list and
1294 self.assertEqual(len(default_pol[key]), 1)
1295 self.assertEqual(len(diff_pol[key]), 1)
1296 self._RecursiveCheckMergedDicts(default_pol[key][0],
1297 diff_pol[key][0], val[0])
1299 self.assertEqual(val, diff_pol[key])
1301 self.assertEqual(val, default_pol[key])
1303 def testClusterPolicy(self):
1304 pol0 = cli.CreateIPolicyFromOpts(
1306 ispecs_cpu_count={},
1307 ispecs_disk_count={},
1308 ispecs_disk_size={},
1309 ispecs_nic_count={},
1310 ipolicy_disk_templates=None,
1311 ipolicy_vcpu_ratio=None,
1312 ipolicy_spindle_ratio=None,
1315 self.assertEqual(pol0, constants.IPOLICY_DEFAULTS)
1318 constants.ISPECS_MINMAX: [
1320 constants.ISPECS_MIN: {
1321 constants.ISPEC_CPU_COUNT: 2,
1322 constants.ISPEC_DISK_COUNT: 1,
1324 constants.ISPECS_MAX: {
1325 constants.ISPEC_MEM_SIZE: 12*1024,
1326 constants.ISPEC_DISK_COUNT: 2,
1330 constants.ISPECS_STD: {
1331 constants.ISPEC_CPU_COUNT: 2,
1332 constants.ISPEC_DISK_COUNT: 2,
1334 constants.IPOLICY_VCPU_RATIO: 3.1,
1336 pol1 = cli.CreateIPolicyFromOpts(
1337 ispecs_mem_size={"max": "12g"},
1338 ispecs_cpu_count={"min": 2, "std": 2},
1339 ispecs_disk_count={"min": 1, "max": 2, "std": 2},
1340 ispecs_disk_size={},
1341 ispecs_nic_count={},
1342 ipolicy_disk_templates=None,
1343 ipolicy_vcpu_ratio=3.1,
1344 ipolicy_spindle_ratio=None,
1347 self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS,
1348 exp_pol1, pol1, merge_minmax=True)
1351 constants.ISPECS_MINMAX: [
1353 constants.ISPECS_MIN: {
1354 constants.ISPEC_DISK_SIZE: 512,
1355 constants.ISPEC_NIC_COUNT: 2,
1357 constants.ISPECS_MAX: {
1358 constants.ISPEC_NIC_COUNT: 3,
1362 constants.ISPECS_STD: {
1363 constants.ISPEC_CPU_COUNT: 2,
1364 constants.ISPEC_NIC_COUNT: 3,
1366 constants.IPOLICY_SPINDLE_RATIO: 1.3,
1367 constants.IPOLICY_DTS: ["templates"],
1369 pol2 = cli.CreateIPolicyFromOpts(
1371 ispecs_cpu_count={"std": 2},
1372 ispecs_disk_count={},
1373 ispecs_disk_size={"min": "0.5g"},
1374 ispecs_nic_count={"min": 2, "max": 3, "std": 3},
1375 ipolicy_disk_templates=["templates"],
1376 ipolicy_vcpu_ratio=None,
1377 ipolicy_spindle_ratio=1.3,
1380 self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS,
1381 exp_pol2, pol2, merge_minmax=True)
1383 for fill_all in [False, True]:
1385 constants.ISPECS_STD: {
1386 constants.ISPEC_CPU_COUNT: 2,
1387 constants.ISPEC_NIC_COUNT: 3,
1390 pol3 = cli.CreateIPolicyFromOpts(
1392 constants.ISPEC_CPU_COUNT: "2",
1393 constants.ISPEC_NIC_COUNT: "3",
1395 ipolicy_disk_templates=None,
1396 ipolicy_vcpu_ratio=None,
1397 ipolicy_spindle_ratio=None,
1401 self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS,
1402 exp_pol3, pol3, merge_minmax=True)
1404 self.assertEqual(pol3, exp_pol3)
1406 def testPartialPolicy(self):
1407 exp_pol0 = objects.MakeEmptyIPolicy()
1408 pol0 = cli.CreateIPolicyFromOpts(
1411 ipolicy_disk_templates=None,
1412 ipolicy_vcpu_ratio=None,
1413 ipolicy_spindle_ratio=None,
1416 self.assertEqual(pol0, exp_pol0)
1419 constants.IPOLICY_VCPU_RATIO: 3.1,
1421 pol1 = cli.CreateIPolicyFromOpts(
1424 ipolicy_disk_templates=None,
1425 ipolicy_vcpu_ratio=3.1,
1426 ipolicy_spindle_ratio=None,
1429 self.assertEqual(pol1, exp_pol1)
1432 constants.IPOLICY_SPINDLE_RATIO: 1.3,
1433 constants.IPOLICY_DTS: ["templates"],
1435 pol2 = cli.CreateIPolicyFromOpts(
1438 ipolicy_disk_templates=["templates"],
1439 ipolicy_vcpu_ratio=None,
1440 ipolicy_spindle_ratio=1.3,
1443 self.assertEqual(pol2, exp_pol2)
1445 def _TestInvalidISpecs(self, minmax_ispecs, std_ispecs, fail=True):
1446 for fill_all in [False, True]:
1448 self.assertRaises((errors.OpPrereqError,
1449 errors.UnitParseError,
1450 errors.TypeEnforcementError),
1451 cli.CreateIPolicyFromOpts,
1452 minmax_ispecs=minmax_ispecs,
1453 std_ispecs=std_ispecs,
1456 cli.CreateIPolicyFromOpts(minmax_ispecs=minmax_ispecs,
1457 std_ispecs=std_ispecs,
1460 def testInvalidPolicies(self):
1461 self.assertRaises(AssertionError, cli.CreateIPolicyFromOpts,
1462 std_ispecs={constants.ISPEC_MEM_SIZE: 1024},
1463 ipolicy_disk_templates=None, ipolicy_vcpu_ratio=None,
1464 ipolicy_spindle_ratio=None, group_ipolicy=True)
1465 self.assertRaises(errors.OpPrereqError, cli.CreateIPolicyFromOpts,
1466 ispecs_mem_size={"wrong": "x"}, ispecs_cpu_count={},
1467 ispecs_disk_count={}, ispecs_disk_size={},
1468 ispecs_nic_count={}, ipolicy_disk_templates=None,
1469 ipolicy_vcpu_ratio=None, ipolicy_spindle_ratio=None,
1471 self.assertRaises(errors.TypeEnforcementError, cli.CreateIPolicyFromOpts,
1472 ispecs_mem_size={}, ispecs_cpu_count={"min": "default"},
1473 ispecs_disk_count={}, ispecs_disk_size={},
1474 ispecs_nic_count={}, ipolicy_disk_templates=None,
1475 ipolicy_vcpu_ratio=None, ipolicy_spindle_ratio=None,
1479 constants.ISPECS_MINMAX_DEFAULTS,
1480 constants.ISPECS_MINMAX_DEFAULTS,
1482 self._TestInvalidISpecs(good_mmspecs, None, fail=False)
1483 broken_mmspecs = copy.deepcopy(good_mmspecs)
1484 for minmaxpair in broken_mmspecs:
1485 for key in constants.ISPECS_MINMAX_KEYS:
1486 for par in constants.ISPECS_PARAMETERS:
1487 old = minmaxpair[key][par]
1488 del minmaxpair[key][par]
1489 self._TestInvalidISpecs(broken_mmspecs, None)
1490 minmaxpair[key][par] = "invalid"
1491 self._TestInvalidISpecs(broken_mmspecs, None)
1492 minmaxpair[key][par] = old
1493 minmaxpair[key]["invalid_key"] = None
1494 self._TestInvalidISpecs(broken_mmspecs, None)
1495 del minmaxpair[key]["invalid_key"]
1496 minmaxpair["invalid_key"] = None
1497 self._TestInvalidISpecs(broken_mmspecs, None)
1498 del minmaxpair["invalid_key"]
1499 assert broken_mmspecs == good_mmspecs
1501 good_stdspecs = constants.IPOLICY_DEFAULTS[constants.ISPECS_STD]
1502 self._TestInvalidISpecs(None, good_stdspecs, fail=False)
1503 broken_stdspecs = copy.deepcopy(good_stdspecs)
1504 for par in constants.ISPECS_PARAMETERS:
1505 old = broken_stdspecs[par]
1506 broken_stdspecs[par] = "invalid"
1507 self._TestInvalidISpecs(None, broken_stdspecs)
1508 broken_stdspecs[par] = old
1509 broken_stdspecs["invalid_key"] = None
1510 self._TestInvalidISpecs(None, broken_stdspecs)
1511 del broken_stdspecs["invalid_key"]
1512 assert broken_stdspecs == good_stdspecs
1514 def testAllowedValues(self):
1517 constants.ISPECS_MINMAX: allowedv,
1518 constants.IPOLICY_DTS: allowedv,
1519 constants.IPOLICY_VCPU_RATIO: allowedv,
1520 constants.IPOLICY_SPINDLE_RATIO: allowedv,
1522 pol1 = cli.CreateIPolicyFromOpts(minmax_ispecs=[{allowedv: {}}],
1524 ipolicy_disk_templates=allowedv,
1525 ipolicy_vcpu_ratio=allowedv,
1526 ipolicy_spindle_ratio=allowedv,
1527 allowed_values=[allowedv])
1528 self.assertEqual(pol1, exp_pol1)
1531 def _ConvertSpecToStrings(spec):
1533 for (par, val) in spec.items():
1537 def _CheckNewStyleSpecsCall(self, exp_ipolicy, minmax_ispecs, std_ispecs,
1538 group_ipolicy, fill_all):
1539 ipolicy = cli.CreateIPolicyFromOpts(minmax_ispecs=minmax_ispecs,
1540 std_ispecs=std_ispecs,
1541 group_ipolicy=group_ipolicy,
1543 self.assertEqual(ipolicy, exp_ipolicy)
1545 def _TestFullISpecsInner(self, skel_exp_ipol, exp_minmax, exp_std,
1546 group_ipolicy, fill_all):
1547 exp_ipol = skel_exp_ipol.copy()
1548 if exp_minmax is not None:
1550 for exp_mm_pair in exp_minmax:
1552 for (key, spec) in exp_mm_pair.items():
1553 mmpair[key] = self._ConvertSpecToStrings(spec)
1554 minmax_ispecs.append(mmpair)
1555 exp_ipol[constants.ISPECS_MINMAX] = exp_minmax
1557 minmax_ispecs = None
1558 if exp_std is not None:
1559 std_ispecs = self._ConvertSpecToStrings(exp_std)
1560 exp_ipol[constants.ISPECS_STD] = exp_std
1564 self._CheckNewStyleSpecsCall(exp_ipol, minmax_ispecs, std_ispecs,
1565 group_ipolicy, fill_all)
1567 for mmpair in minmax_ispecs:
1568 for (key, spec) in mmpair.items():
1569 for par in [constants.ISPEC_MEM_SIZE, constants.ISPEC_DISK_SIZE]:
1572 self._CheckNewStyleSpecsCall(exp_ipol, minmax_ispecs, std_ispecs,
1573 group_ipolicy, fill_all)
1575 for par in [constants.ISPEC_MEM_SIZE, constants.ISPEC_DISK_SIZE]:
1576 if par in std_ispecs:
1577 std_ispecs[par] += "m"
1578 self._CheckNewStyleSpecsCall(exp_ipol, minmax_ispecs, std_ispecs,
1579 group_ipolicy, fill_all)
1581 def testFullISpecs(self):
1584 constants.ISPECS_MIN: {
1585 constants.ISPEC_MEM_SIZE: 512,
1586 constants.ISPEC_CPU_COUNT: 2,
1587 constants.ISPEC_DISK_COUNT: 2,
1588 constants.ISPEC_DISK_SIZE: 512,
1589 constants.ISPEC_NIC_COUNT: 2,
1590 constants.ISPEC_SPINDLE_USE: 2,
1592 constants.ISPECS_MAX: {
1593 constants.ISPEC_MEM_SIZE: 768*1024,
1594 constants.ISPEC_CPU_COUNT: 7,
1595 constants.ISPEC_DISK_COUNT: 6,
1596 constants.ISPEC_DISK_SIZE: 2048*1024,
1597 constants.ISPEC_NIC_COUNT: 3,
1598 constants.ISPEC_SPINDLE_USE: 3,
1604 constants.ISPECS_MIN: {
1605 constants.ISPEC_MEM_SIZE: 512,
1606 constants.ISPEC_CPU_COUNT: 2,
1607 constants.ISPEC_DISK_COUNT: 2,
1608 constants.ISPEC_DISK_SIZE: 512,
1609 constants.ISPEC_NIC_COUNT: 2,
1610 constants.ISPEC_SPINDLE_USE: 2,
1612 constants.ISPECS_MAX: {
1613 constants.ISPEC_MEM_SIZE: 768*1024,
1614 constants.ISPEC_CPU_COUNT: 7,
1615 constants.ISPEC_DISK_COUNT: 6,
1616 constants.ISPEC_DISK_SIZE: 2048*1024,
1617 constants.ISPEC_NIC_COUNT: 3,
1618 constants.ISPEC_SPINDLE_USE: 3,
1622 constants.ISPECS_MIN: {
1623 constants.ISPEC_MEM_SIZE: 1024*1024,
1624 constants.ISPEC_CPU_COUNT: 3,
1625 constants.ISPEC_DISK_COUNT: 3,
1626 constants.ISPEC_DISK_SIZE: 256,
1627 constants.ISPEC_NIC_COUNT: 4,
1628 constants.ISPEC_SPINDLE_USE: 5,
1630 constants.ISPECS_MAX: {
1631 constants.ISPEC_MEM_SIZE: 2048*1024,
1632 constants.ISPEC_CPU_COUNT: 5,
1633 constants.ISPEC_DISK_COUNT: 5,
1634 constants.ISPEC_DISK_SIZE: 1024*1024,
1635 constants.ISPEC_NIC_COUNT: 5,
1636 constants.ISPEC_SPINDLE_USE: 7,
1641 constants.ISPEC_MEM_SIZE: 768*1024,
1642 constants.ISPEC_CPU_COUNT: 7,
1643 constants.ISPEC_DISK_COUNT: 6,
1644 constants.ISPEC_DISK_SIZE: 2048*1024,
1645 constants.ISPEC_NIC_COUNT: 3,
1646 constants.ISPEC_SPINDLE_USE: 1,
1648 for fill_all in [False, True]:
1650 skel_ipolicy = constants.IPOLICY_DEFAULTS
1653 self._TestFullISpecsInner(skel_ipolicy, None, exp_std1,
1655 for exp_minmax in [exp_minmax1, exp_minmax2]:
1656 self._TestFullISpecsInner(skel_ipolicy, exp_minmax, exp_std1,
1658 self._TestFullISpecsInner(skel_ipolicy, exp_minmax, None,
1662 class TestPrintIPolicyCommand(unittest.TestCase):
1663 """Test case for cli.PrintIPolicyCommand"""
1668 _SPECS1_STR = "par1=42,par2=xyz"
1671 "another_param": 101,
1673 _SPECS2_STR = "another_param=101,param=10"
1678 _SPECS3_STR = "par1=1024,param=abc"
1680 def _CheckPrintIPolicyCommand(self, ipolicy, isgroup, expected):
1682 cli.PrintIPolicyCommand(buf, ipolicy, isgroup)
1683 self.assertEqual(buf.getvalue(), expected)
1685 def testIgnoreStdForGroup(self):
1686 self._CheckPrintIPolicyCommand({"std": self._SPECS1}, True, "")
1688 def testIgnoreEmpty(self):
1699 "min": self._SPECS1,
1703 for pol in policies:
1704 self._CheckPrintIPolicyCommand(pol, False, "")
1706 def testFullPolicies(self):
1708 ({"std": self._SPECS1},
1709 " %s %s" % (cli.IPOLICY_STD_SPECS_STR, self._SPECS1_STR)),
1711 "min": self._SPECS1,
1712 "max": self._SPECS2,
1714 " %s min:%s/max:%s" % (cli.IPOLICY_BOUNDS_SPECS_STR,
1715 self._SPECS1_STR, self._SPECS2_STR)),
1718 "min": self._SPECS1,
1719 "max": self._SPECS2,
1722 "min": self._SPECS2,
1723 "max": self._SPECS3,
1726 " %s min:%s/max:%s//min:%s/max:%s" %
1727 (cli.IPOLICY_BOUNDS_SPECS_STR, self._SPECS1_STR, self._SPECS2_STR,
1728 self._SPECS2_STR, self._SPECS3_STR)),
1730 for (pol, exp) in cases:
1731 self._CheckPrintIPolicyCommand(pol, False, exp)
1734 if __name__ == "__main__":
1735 testutils.GanetiTestProgram()