cli: Change “<…>” in query output to “(…)”
[ganeti-local] / test / ganeti.cli_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2008 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script for unittesting the cli module"""
23
24 import unittest
25 from cStringIO import StringIO
26
27 import ganeti
28 import testutils
29
30 from ganeti import constants
31 from ganeti import cli
32 from ganeti import errors
33 from ganeti import utils
34 from ganeti import objects
35 from ganeti.errors import OpPrereqError, ParameterError
36
37
38 class TestParseTimespec(unittest.TestCase):
39   """Testing case for ParseTimespec"""
40
41   def testValidTimes(self):
42     """Test valid timespecs"""
43     test_data = [
44       ('1s', 1),
45       ('1', 1),
46       ('1m', 60),
47       ('1h', 60 * 60),
48       ('1d', 60 * 60 * 24),
49       ('1w', 60 * 60 * 24 * 7),
50       ('4h', 4 * 60 * 60),
51       ('61m', 61 * 60),
52       ]
53     for value, expected_result in test_data:
54       self.failUnlessEqual(cli.ParseTimespec(value), expected_result)
55
56   def testInvalidTime(self):
57     """Test invalid timespecs"""
58     test_data = [
59       '1y',
60       '',
61       'aaa',
62       's',
63       ]
64     for value in test_data:
65       self.failUnlessRaises(OpPrereqError, cli.ParseTimespec, value)
66
67
68 class TestSplitKeyVal(unittest.TestCase):
69   """Testing case for cli._SplitKeyVal"""
70   DATA = "a=b,c,no_d,-e"
71   RESULT = {"a": "b", "c": True, "d": False, "e": None}
72
73   def testSplitKeyVal(self):
74     """Test splitting"""
75     self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA), self.RESULT)
76
77   def testDuplicateParam(self):
78     """Test duplicate parameters"""
79     for data in ("a=1,a=2", "a,no_a"):
80       self.failUnlessRaises(ParameterError, cli._SplitKeyVal,
81                             "option", data)
82
83   def testEmptyData(self):
84     """Test how we handle splitting an empty string"""
85     self.failUnlessEqual(cli._SplitKeyVal("option", ""), {})
86
87 class TestIdentKeyVal(unittest.TestCase):
88   """Testing case for cli.check_ident_key_val"""
89
90   def testIdentKeyVal(self):
91     """Test identkeyval"""
92     def cikv(value):
93       return cli.check_ident_key_val("option", "opt", value)
94
95     self.assertEqual(cikv("foo:bar"), ("foo", {"bar": True}))
96     self.assertEqual(cikv("foo:bar=baz"), ("foo", {"bar": "baz"}))
97     self.assertEqual(cikv("bar:b=c,c=a"), ("bar", {"b": "c", "c": "a"}))
98     self.assertEqual(cikv("no_bar"), ("bar", False))
99     self.assertRaises(ParameterError, cikv, "no_bar:foo")
100     self.assertRaises(ParameterError, cikv, "no_bar:foo=baz")
101     self.assertEqual(cikv("-foo"), ("foo", None))
102     self.assertRaises(ParameterError, cikv, "-foo:a=c")
103
104
105 class TestToStream(unittest.TestCase):
106   """Test the ToStream functions"""
107
108   def testBasic(self):
109     for data in ["foo",
110                  "foo %s",
111                  "foo %(test)s",
112                  "foo %s %s",
113                  "",
114                  ]:
115       buf = StringIO()
116       cli._ToStream(buf, data)
117       self.failUnlessEqual(buf.getvalue(), data+'\n')
118
119   def testParams(self):
120       buf = StringIO()
121       cli._ToStream(buf, "foo %s", 1)
122       self.failUnlessEqual(buf.getvalue(), "foo 1\n")
123       buf = StringIO()
124       cli._ToStream(buf, "foo %s", (15,16))
125       self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n")
126       buf = StringIO()
127       cli._ToStream(buf, "foo %s %s", "a", "b")
128       self.failUnlessEqual(buf.getvalue(), "foo a b\n")
129
130
131 class TestGenerateTable(unittest.TestCase):
132   HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
133
134   FIELDS1 = ["f1", "f2"]
135   DATA1 = [
136     ["abc", 1234],
137     ["foobar", 56],
138     ["b", -14],
139     ]
140
141   def _test(self, headers, fields, separator, data,
142             numfields, unitfields, units, expected):
143     table = cli.GenerateTable(headers, fields, separator, data,
144                               numfields=numfields, unitfields=unitfields,
145                               units=units)
146     self.assertEqual(table, expected)
147
148   def testPlain(self):
149     exp = [
150       "Field1 Field2",
151       "abc    1234",
152       "foobar 56",
153       "b      -14",
154       ]
155     self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
156                None, None, "m", exp)
157
158   def testNoFields(self):
159     self._test(self.HEADERS, [], None, [[], []],
160                None, None, "m", ["", "", ""])
161     self._test(None, [], None, [[], []],
162                None, None, "m", ["", ""])
163
164   def testSeparator(self):
165     for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
166       exp = [
167         "Field1%sField2" % sep,
168         "abc%s1234" % sep,
169         "foobar%s56" % sep,
170         "b%s-14" % sep,
171         ]
172       self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
173                  None, None, "m", exp)
174
175   def testNoHeader(self):
176     exp = [
177       "abc    1234",
178       "foobar 56",
179       "b      -14",
180       ]
181     self._test(None, self.FIELDS1, None, self.DATA1,
182                None, None, "m", exp)
183
184   def testUnknownField(self):
185     headers = {
186       "f1": "Field1",
187       }
188     exp = [
189       "Field1 UNKNOWN",
190       "abc    1234",
191       "foobar 56",
192       "b      -14",
193       ]
194     self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
195                None, None, "m", exp)
196
197   def testNumfields(self):
198     fields = ["f1", "f2", "f3"]
199     data = [
200       ["abc", 1234, 0],
201       ["foobar", 56, 3],
202       ["b", -14, "-"],
203       ]
204     exp = [
205       "Field1 Field2 Field3",
206       "abc      1234      0",
207       "foobar     56      3",
208       "b         -14      -",
209       ]
210     self._test(self.HEADERS, fields, None, data,
211                ["f2", "f3"], None, "m", exp)
212
213   def testUnitfields(self):
214     expnosep = [
215       "Field1 Field2 Field3",
216       "abc      1234     0M",
217       "foobar     56     3M",
218       "b         -14      -",
219       ]
220
221     expsep = [
222       "Field1:Field2:Field3",
223       "abc:1234:0M",
224       "foobar:56:3M",
225       "b:-14:-",
226       ]
227
228     for sep, expected in [(None, expnosep), (":", expsep)]:
229       fields = ["f1", "f2", "f3"]
230       data = [
231         ["abc", 1234, 0],
232         ["foobar", 56, 3],
233         ["b", -14, "-"],
234         ]
235       self._test(self.HEADERS, fields, sep, data,
236                  ["f2", "f3"], ["f3"], "h", expected)
237
238   def testUnusual(self):
239     data = [
240       ["%", "xyz"],
241       ["%%", "abc"],
242       ]
243     exp = [
244       "Field1 Field2",
245       "%      xyz",
246       "%%     abc",
247       ]
248     self._test(self.HEADERS, ["f1", "f2"], None, data,
249                None, None, "m", exp)
250
251
252 class TestFormatQueryResult(unittest.TestCase):
253   def test(self):
254     fields = [
255       objects.QueryFieldDefinition(name="name", title="Name",
256                                    kind=constants.QFT_TEXT),
257       objects.QueryFieldDefinition(name="size", title="Size",
258                                    kind=constants.QFT_NUMBER),
259       objects.QueryFieldDefinition(name="act", title="Active",
260                                    kind=constants.QFT_BOOL),
261       objects.QueryFieldDefinition(name="mem", title="Memory",
262                                    kind=constants.QFT_UNIT),
263       objects.QueryFieldDefinition(name="other", title="SomeList",
264                                    kind=constants.QFT_OTHER),
265       ]
266
267     response = objects.QueryResponse(fields=fields, data=[
268       [(constants.QRFS_NORMAL, "nodeA"), (constants.QRFS_NORMAL, 128),
269        (constants.QRFS_NORMAL, False), (constants.QRFS_NORMAL, 1468006),
270        (constants.QRFS_NORMAL, [])],
271       [(constants.QRFS_NORMAL, "other"), (constants.QRFS_NORMAL, 512),
272        (constants.QRFS_NORMAL, True), (constants.QRFS_NORMAL, 16),
273        (constants.QRFS_NORMAL, [1, 2, 3])],
274       [(constants.QRFS_NORMAL, "xyz"), (constants.QRFS_NORMAL, 1024),
275        (constants.QRFS_NORMAL, True), (constants.QRFS_NORMAL, 4096),
276        (constants.QRFS_NORMAL, [{}, {}])],
277       ])
278
279     self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True),
280       (cli.QR_NORMAL, [
281       "Name  Size Active Memory SomeList",
282       "nodeA  128 N        1.4T []",
283       "other  512 Y         16M [1, 2, 3]",
284       "xyz   1024 Y        4.0G [{}, {}]",
285       ]))
286
287   def testTimestampAndUnit(self):
288     fields = [
289       objects.QueryFieldDefinition(name="name", title="Name",
290                                    kind=constants.QFT_TEXT),
291       objects.QueryFieldDefinition(name="size", title="Size",
292                                    kind=constants.QFT_UNIT),
293       objects.QueryFieldDefinition(name="mtime", title="ModTime",
294                                    kind=constants.QFT_TIMESTAMP),
295       ]
296
297     response = objects.QueryResponse(fields=fields, data=[
298       [(constants.QRFS_NORMAL, "a"), (constants.QRFS_NORMAL, 1024),
299        (constants.QRFS_NORMAL, 0)],
300       [(constants.QRFS_NORMAL, "b"), (constants.QRFS_NORMAL, 144996),
301        (constants.QRFS_NORMAL, 1291746295)],
302       ])
303
304     self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True),
305       (cli.QR_NORMAL, [
306       "Name   Size ModTime",
307       "a      1024 %s" % utils.FormatTime(0),
308       "b    144996 %s" % utils.FormatTime(1291746295),
309       ]))
310
311   def testOverride(self):
312     fields = [
313       objects.QueryFieldDefinition(name="name", title="Name",
314                                    kind=constants.QFT_TEXT),
315       objects.QueryFieldDefinition(name="cust", title="Custom",
316                                    kind=constants.QFT_OTHER),
317       objects.QueryFieldDefinition(name="xt", title="XTime",
318                                    kind=constants.QFT_TIMESTAMP),
319       ]
320
321     response = objects.QueryResponse(fields=fields, data=[
322       [(constants.QRFS_NORMAL, "x"), (constants.QRFS_NORMAL, ["a", "b", "c"]),
323        (constants.QRFS_NORMAL, 1234)],
324       [(constants.QRFS_NORMAL, "y"), (constants.QRFS_NORMAL, range(10)),
325        (constants.QRFS_NORMAL, 1291746295)],
326       ])
327
328     override = {
329       "cust": (utils.CommaJoin, False),
330       "xt": (hex, True),
331       }
332
333     self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True,
334                                            format_override=override),
335       (cli.QR_NORMAL, [
336       "Name Custom                            XTime",
337       "x    a, b, c                           0x4d2",
338       "y    0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
339       ]))
340
341   def testSeparator(self):
342     fields = [
343       objects.QueryFieldDefinition(name="name", title="Name",
344                                    kind=constants.QFT_TEXT),
345       objects.QueryFieldDefinition(name="count", title="Count",
346                                    kind=constants.QFT_NUMBER),
347       objects.QueryFieldDefinition(name="desc", title="Description",
348                                    kind=constants.QFT_TEXT),
349       ]
350
351     response = objects.QueryResponse(fields=fields, data=[
352       [(constants.QRFS_NORMAL, "instance1.example.com"),
353        (constants.QRFS_NORMAL, 21125), (constants.QRFS_NORMAL, "Hello World!")],
354       [(constants.QRFS_NORMAL, "mail.other.net"),
355        (constants.QRFS_NORMAL, -9000), (constants.QRFS_NORMAL, "a,b,c")],
356       ])
357
358     for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]:
359       for header in [None, "Name%sCount%sDescription" % (sep, sep)]:
360         exp = []
361         if header:
362           exp.append(header)
363         exp.extend([
364           "instance1.example.com%s21125%sHello World!" % (sep, sep),
365           "mail.other.net%s-9000%sa,b,c" % (sep, sep),
366           ])
367
368         self.assertEqual(cli.FormatQueryResult(response, separator=sep,
369                                                header=bool(header)),
370                          (cli.QR_NORMAL, exp))
371
372   def testStatusWithUnknown(self):
373     fields = [
374       objects.QueryFieldDefinition(name="id", title="ID",
375                                    kind=constants.QFT_NUMBER),
376       objects.QueryFieldDefinition(name="unk", title="unk",
377                                    kind=constants.QFT_UNKNOWN),
378       objects.QueryFieldDefinition(name="unavail", title="Unavail",
379                                    kind=constants.QFT_BOOL),
380       objects.QueryFieldDefinition(name="nodata", title="NoData",
381                                    kind=constants.QFT_TEXT),
382       ]
383
384     response = objects.QueryResponse(fields=fields, data=[
385       [(constants.QRFS_NORMAL, 1), (constants.QRFS_UNKNOWN, None),
386        (constants.QRFS_NORMAL, False), (constants.QRFS_NORMAL, "")],
387       [(constants.QRFS_NORMAL, 2), (constants.QRFS_UNKNOWN, None),
388        (constants.QRFS_NODATA, None), (constants.QRFS_NORMAL, "x")],
389       [(constants.QRFS_NORMAL, 3), (constants.QRFS_UNKNOWN, None),
390        (constants.QRFS_NORMAL, False), (constants.QRFS_UNAVAIL, None)],
391       ])
392
393     self.assertEqual(cli.FormatQueryResult(response, header=True,
394                                            separator="|"),
395       (cli.QR_UNKNOWN, [
396       "ID|unk|Unavail|NoData",
397       "1|(unknown)|N|",
398       "2|(unknown)|(nodata)|x",
399       "3|(unknown)|N|(unavail)",
400       ]))
401
402   def testNoData(self):
403     fields = [
404       objects.QueryFieldDefinition(name="id", title="ID",
405                                    kind=constants.QFT_NUMBER),
406       objects.QueryFieldDefinition(name="name", title="Name",
407                                    kind=constants.QFT_TEXT),
408       ]
409
410     response = objects.QueryResponse(fields=fields, data=[])
411
412     self.assertEqual(cli.FormatQueryResult(response, header=True),
413                      (cli.QR_NORMAL, ["ID Name"]))
414
415   def testNoDataWithUnknown(self):
416     fields = [
417       objects.QueryFieldDefinition(name="id", title="ID",
418                                    kind=constants.QFT_NUMBER),
419       objects.QueryFieldDefinition(name="unk", title="unk",
420                                    kind=constants.QFT_UNKNOWN),
421       ]
422
423     response = objects.QueryResponse(fields=fields, data=[])
424
425     self.assertEqual(cli.FormatQueryResult(response, header=False),
426                      (cli.QR_UNKNOWN, []))
427
428   def testStatus(self):
429     fields = [
430       objects.QueryFieldDefinition(name="id", title="ID",
431                                    kind=constants.QFT_NUMBER),
432       objects.QueryFieldDefinition(name="unavail", title="Unavail",
433                                    kind=constants.QFT_BOOL),
434       objects.QueryFieldDefinition(name="nodata", title="NoData",
435                                    kind=constants.QFT_TEXT),
436       ]
437
438     response = objects.QueryResponse(fields=fields, data=[
439       [(constants.QRFS_NORMAL, 1), (constants.QRFS_NORMAL, False),
440        (constants.QRFS_NORMAL, "")],
441       [(constants.QRFS_NORMAL, 2), (constants.QRFS_NODATA, None),
442        (constants.QRFS_NORMAL, "x")],
443       [(constants.QRFS_NORMAL, 3), (constants.QRFS_NORMAL, False),
444        (constants.QRFS_UNAVAIL, None)],
445       ])
446
447     self.assertEqual(cli.FormatQueryResult(response, header=False,
448                                            separator="|"),
449       (cli.QR_INCOMPLETE, [
450       "1|N|",
451       "2|(nodata)|x",
452       "3|N|(unavail)",
453       ]))
454
455   def testInvalidFieldType(self):
456     fields = [
457       objects.QueryFieldDefinition(name="x", title="x",
458                                    kind="#some#other#type"),
459       ]
460
461     response = objects.QueryResponse(fields=fields, data=[])
462
463     self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
464
465   def testInvalidFieldStatus(self):
466     fields = [
467       objects.QueryFieldDefinition(name="x", title="x",
468                                    kind=constants.QFT_TEXT),
469       ]
470
471     response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
472     self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
473
474     response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
475     self.assertRaises(AssertionError, cli.FormatQueryResult, response)
476
477   def testEmptyFieldTitle(self):
478     fields = [
479       objects.QueryFieldDefinition(name="x", title="",
480                                    kind=constants.QFT_TEXT),
481       ]
482
483     response = objects.QueryResponse(fields=fields, data=[])
484     self.assertRaises(AssertionError, cli.FormatQueryResult, response)
485
486
487 class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
488   def __init__(self, tc, job_id):
489     self.tc = tc
490     self.job_id = job_id
491     self._wfjcr = []
492     self._jobstatus = []
493     self._expect_notchanged = False
494     self._expect_log = []
495
496   def CheckEmpty(self):
497     self.tc.assertFalse(self._wfjcr)
498     self.tc.assertFalse(self._jobstatus)
499     self.tc.assertFalse(self._expect_notchanged)
500     self.tc.assertFalse(self._expect_log)
501
502   def AddWfjcResult(self, *args):
503     self._wfjcr.append(args)
504
505   def AddQueryJobsResult(self, *args):
506     self._jobstatus.append(args)
507
508   def WaitForJobChangeOnce(self, job_id, fields,
509                            prev_job_info, prev_log_serial):
510     self.tc.assertEqual(job_id, self.job_id)
511     self.tc.assertEqualValues(fields, ["status"])
512     self.tc.assertFalse(self._expect_notchanged)
513     self.tc.assertFalse(self._expect_log)
514
515     (exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0)
516     self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
517     self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
518
519     if result == constants.JOB_NOTCHANGED:
520       self._expect_notchanged = True
521     elif result:
522       (_, logmsgs) = result
523       if logmsgs:
524         self._expect_log.extend(logmsgs)
525
526     return result
527
528   def QueryJobs(self, job_ids, fields):
529     self.tc.assertEqual(job_ids, [self.job_id])
530     self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"])
531     self.tc.assertFalse(self._expect_notchanged)
532     self.tc.assertFalse(self._expect_log)
533
534     result = self._jobstatus.pop(0)
535     self.tc.assertEqual(len(fields), len(result))
536     return [result]
537
538   def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
539     self.tc.assertEqual(job_id, self.job_id)
540     self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
541                               self._expect_log.pop(0))
542
543   def ReportNotChanged(self, job_id, status):
544     self.tc.assertEqual(job_id, self.job_id)
545     self.tc.assert_(self._expect_notchanged)
546     self._expect_notchanged = False
547
548
549 class TestGenericPollJob(testutils.GanetiTestCase):
550   def testSuccessWithLog(self):
551     job_id = 29609
552     cbs = _MockJobPollCb(self, job_id)
553
554     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
555
556     cbs.AddWfjcResult(None, None,
557                       ((constants.JOB_STATUS_QUEUED, ), None))
558
559     cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
560                       constants.JOB_NOTCHANGED)
561
562     cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
563                       ((constants.JOB_STATUS_RUNNING, ),
564                        [(1, utils.SplitTime(1273491611.0),
565                          constants.ELOG_MESSAGE, "Step 1"),
566                         (2, utils.SplitTime(1273491615.9),
567                          constants.ELOG_MESSAGE, "Step 2"),
568                         (3, utils.SplitTime(1273491625.02),
569                          constants.ELOG_MESSAGE, "Step 3"),
570                         (4, utils.SplitTime(1273491635.05),
571                          constants.ELOG_MESSAGE, "Step 4"),
572                         (37, utils.SplitTime(1273491645.0),
573                          constants.ELOG_MESSAGE, "Step 5"),
574                         (203, utils.SplitTime(127349155.0),
575                          constants.ELOG_MESSAGE, "Step 6")]))
576
577     cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
578                       ((constants.JOB_STATUS_RUNNING, ),
579                        [(300, utils.SplitTime(1273491711.01),
580                          constants.ELOG_MESSAGE, "Step X"),
581                         (302, utils.SplitTime(1273491815.8),
582                          constants.ELOG_MESSAGE, "Step Y"),
583                         (303, utils.SplitTime(1273491925.32),
584                          constants.ELOG_MESSAGE, "Step Z")]))
585
586     cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
587                       ((constants.JOB_STATUS_SUCCESS, ), None))
588
589     cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
590                            [constants.OP_STATUS_SUCCESS,
591                             constants.OP_STATUS_SUCCESS],
592                            ["Hello World", "Foo man bar"])
593
594     self.assertEqual(["Hello World", "Foo man bar"],
595                      cli.GenericPollJob(job_id, cbs, cbs))
596     cbs.CheckEmpty()
597
598   def testJobLost(self):
599     job_id = 13746
600
601     cbs = _MockJobPollCb(self, job_id)
602     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
603     cbs.AddWfjcResult(None, None, None)
604     self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
605     cbs.CheckEmpty()
606
607   def testError(self):
608     job_id = 31088
609
610     cbs = _MockJobPollCb(self, job_id)
611     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
612     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
613     cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
614                            [constants.OP_STATUS_SUCCESS,
615                             constants.OP_STATUS_ERROR],
616                            ["Hello World", "Error code 123"])
617     self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
618     cbs.CheckEmpty()
619
620   def testError2(self):
621     job_id = 22235
622
623     cbs = _MockJobPollCb(self, job_id)
624     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
625     encexc = errors.EncodeException(errors.LockError("problem"))
626     cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
627                            [constants.OP_STATUS_ERROR], [encexc])
628     self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
629     cbs.CheckEmpty()
630
631   def testWeirdError(self):
632     job_id = 28847
633
634     cbs = _MockJobPollCb(self, job_id)
635     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
636     cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
637                            [constants.OP_STATUS_RUNNING,
638                             constants.OP_STATUS_RUNNING],
639                            [None, None])
640     self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
641     cbs.CheckEmpty()
642
643   def testCancel(self):
644     job_id = 4275
645
646     cbs = _MockJobPollCb(self, job_id)
647     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
648     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None))
649     cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING,
650                            [constants.OP_STATUS_CANCELING,
651                             constants.OP_STATUS_CANCELING],
652                            [None, None])
653     self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
654     cbs.CheckEmpty()
655
656
657 class TestFormatLogMessage(unittest.TestCase):
658   def test(self):
659     self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
660                                           "Hello World"),
661                      "Hello World")
662     self.assertRaises(TypeError, cli.FormatLogMessage,
663                       constants.ELOG_MESSAGE, [1, 2, 3])
664
665     self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
666
667
668 class TestParseFields(unittest.TestCase):
669   def test(self):
670     self.assertEqual(cli.ParseFields(None, []), [])
671     self.assertEqual(cli.ParseFields("name,foo,hello", []),
672                      ["name", "foo", "hello"])
673     self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]),
674                      ["def", "ault", "fields", "here"])
675     self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]),
676                      ["name", "foo"])
677     self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
678                      ["def", "ault", "name", "foo"])
679
680
681 class TestConstants(unittest.TestCase):
682   def testPriority(self):
683     self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()),
684                      set(constants.OP_PRIO_SUBMIT_VALID))
685     self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES),
686                      sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True))
687
688
689 class TestParseNicOption(unittest.TestCase):
690   def test(self):
691     self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]),
692                      [{ "link": "eth0", }])
693     self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]),
694                      [{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }])
695
696   def testErrors(self):
697     for i in [None, "", "abc", "zero", "Hello World", "\0", []]:
698       self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
699                         [(i, { "link": "eth0", })])
700       self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
701                         [("0", i)])
702
703     self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
704                       [(0, { True: False, })])
705
706     self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
707                       [(3, { "mode": [], })])
708
709
710 if __name__ == '__main__':
711   testutils.GanetiTestProgram()