query2: Add new field status “offline”
[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       objects.QueryFieldDefinition(name="offline", title="OffLine",
383                                    kind=constants.QFT_TEXT),
384       ]
385
386     response = objects.QueryResponse(fields=fields, data=[
387       [(constants.QRFS_NORMAL, 1), (constants.QRFS_UNKNOWN, None),
388        (constants.QRFS_NORMAL, False), (constants.QRFS_NORMAL, ""),
389        (constants.QRFS_OFFLINE, None)],
390       [(constants.QRFS_NORMAL, 2), (constants.QRFS_UNKNOWN, None),
391        (constants.QRFS_NODATA, None), (constants.QRFS_NORMAL, "x"),
392        (constants.QRFS_OFFLINE, None)],
393       [(constants.QRFS_NORMAL, 3), (constants.QRFS_UNKNOWN, None),
394        (constants.QRFS_NORMAL, False), (constants.QRFS_UNAVAIL, None),
395        (constants.QRFS_OFFLINE, None)],
396       ])
397
398     self.assertEqual(cli.FormatQueryResult(response, header=True,
399                                            separator="|"),
400       (cli.QR_UNKNOWN, [
401       "ID|unk|Unavail|NoData|OffLine",
402       "1|(unknown)|N||(offline)",
403       "2|(unknown)|(nodata)|x|(offline)",
404       "3|(unknown)|N|(unavail)|(offline)",
405       ]))
406
407   def testNoData(self):
408     fields = [
409       objects.QueryFieldDefinition(name="id", title="ID",
410                                    kind=constants.QFT_NUMBER),
411       objects.QueryFieldDefinition(name="name", title="Name",
412                                    kind=constants.QFT_TEXT),
413       ]
414
415     response = objects.QueryResponse(fields=fields, data=[])
416
417     self.assertEqual(cli.FormatQueryResult(response, header=True),
418                      (cli.QR_NORMAL, ["ID Name"]))
419
420   def testNoDataWithUnknown(self):
421     fields = [
422       objects.QueryFieldDefinition(name="id", title="ID",
423                                    kind=constants.QFT_NUMBER),
424       objects.QueryFieldDefinition(name="unk", title="unk",
425                                    kind=constants.QFT_UNKNOWN),
426       ]
427
428     response = objects.QueryResponse(fields=fields, data=[])
429
430     self.assertEqual(cli.FormatQueryResult(response, header=False),
431                      (cli.QR_UNKNOWN, []))
432
433   def testStatus(self):
434     fields = [
435       objects.QueryFieldDefinition(name="id", title="ID",
436                                    kind=constants.QFT_NUMBER),
437       objects.QueryFieldDefinition(name="unavail", title="Unavail",
438                                    kind=constants.QFT_BOOL),
439       objects.QueryFieldDefinition(name="nodata", title="NoData",
440                                    kind=constants.QFT_TEXT),
441       objects.QueryFieldDefinition(name="offline", title="OffLine",
442                                    kind=constants.QFT_TEXT),
443       ]
444
445     response = objects.QueryResponse(fields=fields, data=[
446       [(constants.QRFS_NORMAL, 1), (constants.QRFS_NORMAL, False),
447        (constants.QRFS_NORMAL, ""), (constants.QRFS_OFFLINE, None)],
448       [(constants.QRFS_NORMAL, 2), (constants.QRFS_NODATA, None),
449        (constants.QRFS_NORMAL, "x"), (constants.QRFS_NORMAL, "abc")],
450       [(constants.QRFS_NORMAL, 3), (constants.QRFS_NORMAL, False),
451        (constants.QRFS_UNAVAIL, None), (constants.QRFS_OFFLINE, None)],
452       ])
453
454     self.assertEqual(cli.FormatQueryResult(response, header=False,
455                                            separator="|"),
456       (cli.QR_INCOMPLETE, [
457       "1|N||(offline)",
458       "2|(nodata)|x|abc",
459       "3|N|(unavail)|(offline)",
460       ]))
461
462   def testInvalidFieldType(self):
463     fields = [
464       objects.QueryFieldDefinition(name="x", title="x",
465                                    kind="#some#other#type"),
466       ]
467
468     response = objects.QueryResponse(fields=fields, data=[])
469
470     self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
471
472   def testInvalidFieldStatus(self):
473     fields = [
474       objects.QueryFieldDefinition(name="x", title="x",
475                                    kind=constants.QFT_TEXT),
476       ]
477
478     response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
479     self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
480
481     response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
482     self.assertRaises(AssertionError, cli.FormatQueryResult, response)
483
484   def testEmptyFieldTitle(self):
485     fields = [
486       objects.QueryFieldDefinition(name="x", title="",
487                                    kind=constants.QFT_TEXT),
488       ]
489
490     response = objects.QueryResponse(fields=fields, data=[])
491     self.assertRaises(AssertionError, cli.FormatQueryResult, response)
492
493
494 class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
495   def __init__(self, tc, job_id):
496     self.tc = tc
497     self.job_id = job_id
498     self._wfjcr = []
499     self._jobstatus = []
500     self._expect_notchanged = False
501     self._expect_log = []
502
503   def CheckEmpty(self):
504     self.tc.assertFalse(self._wfjcr)
505     self.tc.assertFalse(self._jobstatus)
506     self.tc.assertFalse(self._expect_notchanged)
507     self.tc.assertFalse(self._expect_log)
508
509   def AddWfjcResult(self, *args):
510     self._wfjcr.append(args)
511
512   def AddQueryJobsResult(self, *args):
513     self._jobstatus.append(args)
514
515   def WaitForJobChangeOnce(self, job_id, fields,
516                            prev_job_info, prev_log_serial):
517     self.tc.assertEqual(job_id, self.job_id)
518     self.tc.assertEqualValues(fields, ["status"])
519     self.tc.assertFalse(self._expect_notchanged)
520     self.tc.assertFalse(self._expect_log)
521
522     (exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0)
523     self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
524     self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
525
526     if result == constants.JOB_NOTCHANGED:
527       self._expect_notchanged = True
528     elif result:
529       (_, logmsgs) = result
530       if logmsgs:
531         self._expect_log.extend(logmsgs)
532
533     return result
534
535   def QueryJobs(self, job_ids, fields):
536     self.tc.assertEqual(job_ids, [self.job_id])
537     self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"])
538     self.tc.assertFalse(self._expect_notchanged)
539     self.tc.assertFalse(self._expect_log)
540
541     result = self._jobstatus.pop(0)
542     self.tc.assertEqual(len(fields), len(result))
543     return [result]
544
545   def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
546     self.tc.assertEqual(job_id, self.job_id)
547     self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
548                               self._expect_log.pop(0))
549
550   def ReportNotChanged(self, job_id, status):
551     self.tc.assertEqual(job_id, self.job_id)
552     self.tc.assert_(self._expect_notchanged)
553     self._expect_notchanged = False
554
555
556 class TestGenericPollJob(testutils.GanetiTestCase):
557   def testSuccessWithLog(self):
558     job_id = 29609
559     cbs = _MockJobPollCb(self, job_id)
560
561     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
562
563     cbs.AddWfjcResult(None, None,
564                       ((constants.JOB_STATUS_QUEUED, ), None))
565
566     cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
567                       constants.JOB_NOTCHANGED)
568
569     cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
570                       ((constants.JOB_STATUS_RUNNING, ),
571                        [(1, utils.SplitTime(1273491611.0),
572                          constants.ELOG_MESSAGE, "Step 1"),
573                         (2, utils.SplitTime(1273491615.9),
574                          constants.ELOG_MESSAGE, "Step 2"),
575                         (3, utils.SplitTime(1273491625.02),
576                          constants.ELOG_MESSAGE, "Step 3"),
577                         (4, utils.SplitTime(1273491635.05),
578                          constants.ELOG_MESSAGE, "Step 4"),
579                         (37, utils.SplitTime(1273491645.0),
580                          constants.ELOG_MESSAGE, "Step 5"),
581                         (203, utils.SplitTime(127349155.0),
582                          constants.ELOG_MESSAGE, "Step 6")]))
583
584     cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
585                       ((constants.JOB_STATUS_RUNNING, ),
586                        [(300, utils.SplitTime(1273491711.01),
587                          constants.ELOG_MESSAGE, "Step X"),
588                         (302, utils.SplitTime(1273491815.8),
589                          constants.ELOG_MESSAGE, "Step Y"),
590                         (303, utils.SplitTime(1273491925.32),
591                          constants.ELOG_MESSAGE, "Step Z")]))
592
593     cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
594                       ((constants.JOB_STATUS_SUCCESS, ), None))
595
596     cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
597                            [constants.OP_STATUS_SUCCESS,
598                             constants.OP_STATUS_SUCCESS],
599                            ["Hello World", "Foo man bar"])
600
601     self.assertEqual(["Hello World", "Foo man bar"],
602                      cli.GenericPollJob(job_id, cbs, cbs))
603     cbs.CheckEmpty()
604
605   def testJobLost(self):
606     job_id = 13746
607
608     cbs = _MockJobPollCb(self, job_id)
609     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
610     cbs.AddWfjcResult(None, None, None)
611     self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
612     cbs.CheckEmpty()
613
614   def testError(self):
615     job_id = 31088
616
617     cbs = _MockJobPollCb(self, job_id)
618     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
619     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
620     cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
621                            [constants.OP_STATUS_SUCCESS,
622                             constants.OP_STATUS_ERROR],
623                            ["Hello World", "Error code 123"])
624     self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
625     cbs.CheckEmpty()
626
627   def testError2(self):
628     job_id = 22235
629
630     cbs = _MockJobPollCb(self, job_id)
631     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
632     encexc = errors.EncodeException(errors.LockError("problem"))
633     cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
634                            [constants.OP_STATUS_ERROR], [encexc])
635     self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
636     cbs.CheckEmpty()
637
638   def testWeirdError(self):
639     job_id = 28847
640
641     cbs = _MockJobPollCb(self, job_id)
642     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
643     cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
644                            [constants.OP_STATUS_RUNNING,
645                             constants.OP_STATUS_RUNNING],
646                            [None, None])
647     self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
648     cbs.CheckEmpty()
649
650   def testCancel(self):
651     job_id = 4275
652
653     cbs = _MockJobPollCb(self, job_id)
654     cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
655     cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None))
656     cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING,
657                            [constants.OP_STATUS_CANCELING,
658                             constants.OP_STATUS_CANCELING],
659                            [None, None])
660     self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
661     cbs.CheckEmpty()
662
663
664 class TestFormatLogMessage(unittest.TestCase):
665   def test(self):
666     self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
667                                           "Hello World"),
668                      "Hello World")
669     self.assertRaises(TypeError, cli.FormatLogMessage,
670                       constants.ELOG_MESSAGE, [1, 2, 3])
671
672     self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
673
674
675 class TestParseFields(unittest.TestCase):
676   def test(self):
677     self.assertEqual(cli.ParseFields(None, []), [])
678     self.assertEqual(cli.ParseFields("name,foo,hello", []),
679                      ["name", "foo", "hello"])
680     self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]),
681                      ["def", "ault", "fields", "here"])
682     self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]),
683                      ["name", "foo"])
684     self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
685                      ["def", "ault", "name", "foo"])
686
687
688 class TestConstants(unittest.TestCase):
689   def testPriority(self):
690     self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()),
691                      set(constants.OP_PRIO_SUBMIT_VALID))
692     self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES),
693                      sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True))
694
695
696 class TestParseNicOption(unittest.TestCase):
697   def test(self):
698     self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]),
699                      [{ "link": "eth0", }])
700     self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]),
701                      [{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }])
702
703   def testErrors(self):
704     for i in [None, "", "abc", "zero", "Hello World", "\0", []]:
705       self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
706                         [(i, { "link": "eth0", })])
707       self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
708                         [("0", i)])
709
710     self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
711                       [(0, { True: False, })])
712
713     self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
714                       [(3, { "mode": [], })])
715
716
717 if __name__ == '__main__':
718   testutils.GanetiTestProgram()