Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.cli_unittest.py @ 05484a24

History | View | Annotate | Download (31.2 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2008, 2011 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 import qlang
36
from ganeti.errors import OpPrereqError, ParameterError
37

    
38

    
39
class TestParseTimespec(unittest.TestCase):
40
  """Testing case for ParseTimespec"""
41

    
42
  def testValidTimes(self):
43
    """Test valid timespecs"""
44
    test_data = [
45
      ('1s', 1),
46
      ('1', 1),
47
      ('1m', 60),
48
      ('1h', 60 * 60),
49
      ('1d', 60 * 60 * 24),
50
      ('1w', 60 * 60 * 24 * 7),
51
      ('4h', 4 * 60 * 60),
52
      ('61m', 61 * 60),
53
      ]
54
    for value, expected_result in test_data:
55
      self.failUnlessEqual(cli.ParseTimespec(value), expected_result)
56

    
57
  def testInvalidTime(self):
58
    """Test invalid timespecs"""
59
    test_data = [
60
      '1y',
61
      '',
62
      'aaa',
63
      's',
64
      ]
65
    for value in test_data:
66
      self.failUnlessRaises(OpPrereqError, cli.ParseTimespec, value)
67

    
68

    
69
class TestSplitKeyVal(unittest.TestCase):
70
  """Testing case for cli._SplitKeyVal"""
71
  DATA = "a=b,c,no_d,-e"
72
  RESULT = {"a": "b", "c": True, "d": False, "e": None}
73

    
74
  def testSplitKeyVal(self):
75
    """Test splitting"""
76
    self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA), self.RESULT)
77

    
78
  def testDuplicateParam(self):
79
    """Test duplicate parameters"""
80
    for data in ("a=1,a=2", "a,no_a"):
81
      self.failUnlessRaises(ParameterError, cli._SplitKeyVal,
82
                            "option", data)
83

    
84
  def testEmptyData(self):
85
    """Test how we handle splitting an empty string"""
86
    self.failUnlessEqual(cli._SplitKeyVal("option", ""), {})
87

    
88
class TestIdentKeyVal(unittest.TestCase):
89
  """Testing case for cli.check_ident_key_val"""
90

    
91
  def testIdentKeyVal(self):
92
    """Test identkeyval"""
93
    def cikv(value):
94
      return cli.check_ident_key_val("option", "opt", value)
95

    
96
    self.assertEqual(cikv("foo:bar"), ("foo", {"bar": True}))
97
    self.assertEqual(cikv("foo:bar=baz"), ("foo", {"bar": "baz"}))
98
    self.assertEqual(cikv("bar:b=c,c=a"), ("bar", {"b": "c", "c": "a"}))
99
    self.assertEqual(cikv("no_bar"), ("bar", False))
100
    self.assertRaises(ParameterError, cikv, "no_bar:foo")
101
    self.assertRaises(ParameterError, cikv, "no_bar:foo=baz")
102
    self.assertEqual(cikv("-foo"), ("foo", None))
103
    self.assertRaises(ParameterError, cikv, "-foo:a=c")
104

    
105

    
106
class TestToStream(unittest.TestCase):
107
  """Test the ToStream functions"""
108

    
109
  def testBasic(self):
110
    for data in ["foo",
111
                 "foo %s",
112
                 "foo %(test)s",
113
                 "foo %s %s",
114
                 "",
115
                 ]:
116
      buf = StringIO()
117
      cli._ToStream(buf, data)
118
      self.failUnlessEqual(buf.getvalue(), data+'\n')
119

    
120
  def testParams(self):
121
      buf = StringIO()
122
      cli._ToStream(buf, "foo %s", 1)
123
      self.failUnlessEqual(buf.getvalue(), "foo 1\n")
124
      buf = StringIO()
125
      cli._ToStream(buf, "foo %s", (15,16))
126
      self.failUnlessEqual(buf.getvalue(), "foo (15, 16)\n")
127
      buf = StringIO()
128
      cli._ToStream(buf, "foo %s %s", "a", "b")
129
      self.failUnlessEqual(buf.getvalue(), "foo a b\n")
130

    
131

    
132
class TestGenerateTable(unittest.TestCase):
133
  HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
134

    
135
  FIELDS1 = ["f1", "f2"]
136
  DATA1 = [
137
    ["abc", 1234],
138
    ["foobar", 56],
139
    ["b", -14],
140
    ]
141

    
142
  def _test(self, headers, fields, separator, data,
143
            numfields, unitfields, units, expected):
144
    table = cli.GenerateTable(headers, fields, separator, data,
145
                              numfields=numfields, unitfields=unitfields,
146
                              units=units)
147
    self.assertEqual(table, expected)
148

    
149
  def testPlain(self):
150
    exp = [
151
      "Field1 Field2",
152
      "abc    1234",
153
      "foobar 56",
154
      "b      -14",
155
      ]
156
    self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
157
               None, None, "m", exp)
158

    
159
  def testNoFields(self):
160
    self._test(self.HEADERS, [], None, [[], []],
161
               None, None, "m", ["", "", ""])
162
    self._test(None, [], None, [[], []],
163
               None, None, "m", ["", ""])
164

    
165
  def testSeparator(self):
166
    for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
167
      exp = [
168
        "Field1%sField2" % sep,
169
        "abc%s1234" % sep,
170
        "foobar%s56" % sep,
171
        "b%s-14" % sep,
172
        ]
173
      self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
174
                 None, None, "m", exp)
175

    
176
  def testNoHeader(self):
177
    exp = [
178
      "abc    1234",
179
      "foobar 56",
180
      "b      -14",
181
      ]
182
    self._test(None, self.FIELDS1, None, self.DATA1,
183
               None, None, "m", exp)
184

    
185
  def testUnknownField(self):
186
    headers = {
187
      "f1": "Field1",
188
      }
189
    exp = [
190
      "Field1 UNKNOWN",
191
      "abc    1234",
192
      "foobar 56",
193
      "b      -14",
194
      ]
195
    self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
196
               None, None, "m", exp)
197

    
198
  def testNumfields(self):
199
    fields = ["f1", "f2", "f3"]
200
    data = [
201
      ["abc", 1234, 0],
202
      ["foobar", 56, 3],
203
      ["b", -14, "-"],
204
      ]
205
    exp = [
206
      "Field1 Field2 Field3",
207
      "abc      1234      0",
208
      "foobar     56      3",
209
      "b         -14      -",
210
      ]
211
    self._test(self.HEADERS, fields, None, data,
212
               ["f2", "f3"], None, "m", exp)
213

    
214
  def testUnitfields(self):
215
    expnosep = [
216
      "Field1 Field2 Field3",
217
      "abc      1234     0M",
218
      "foobar     56     3M",
219
      "b         -14      -",
220
      ]
221

    
222
    expsep = [
223
      "Field1:Field2:Field3",
224
      "abc:1234:0M",
225
      "foobar:56:3M",
226
      "b:-14:-",
227
      ]
228

    
229
    for sep, expected in [(None, expnosep), (":", expsep)]:
230
      fields = ["f1", "f2", "f3"]
231
      data = [
232
        ["abc", 1234, 0],
233
        ["foobar", 56, 3],
234
        ["b", -14, "-"],
235
        ]
236
      self._test(self.HEADERS, fields, sep, data,
237
                 ["f2", "f3"], ["f3"], "h", expected)
238

    
239
  def testUnusual(self):
240
    data = [
241
      ["%", "xyz"],
242
      ["%%", "abc"],
243
      ]
244
    exp = [
245
      "Field1 Field2",
246
      "%      xyz",
247
      "%%     abc",
248
      ]
249
    self._test(self.HEADERS, ["f1", "f2"], None, data,
250
               None, None, "m", exp)
251

    
252

    
253
class TestFormatQueryResult(unittest.TestCase):
254
  def test(self):
255
    fields = [
256
      objects.QueryFieldDefinition(name="name", title="Name",
257
                                   kind=constants.QFT_TEXT),
258
      objects.QueryFieldDefinition(name="size", title="Size",
259
                                   kind=constants.QFT_NUMBER),
260
      objects.QueryFieldDefinition(name="act", title="Active",
261
                                   kind=constants.QFT_BOOL),
262
      objects.QueryFieldDefinition(name="mem", title="Memory",
263
                                   kind=constants.QFT_UNIT),
264
      objects.QueryFieldDefinition(name="other", title="SomeList",
265
                                   kind=constants.QFT_OTHER),
266
      ]
267

    
268
    response = objects.QueryResponse(fields=fields, data=[
269
      [(constants.RS_NORMAL, "nodeA"), (constants.RS_NORMAL, 128),
270
       (constants.RS_NORMAL, False), (constants.RS_NORMAL, 1468006),
271
       (constants.RS_NORMAL, [])],
272
      [(constants.RS_NORMAL, "other"), (constants.RS_NORMAL, 512),
273
       (constants.RS_NORMAL, True), (constants.RS_NORMAL, 16),
274
       (constants.RS_NORMAL, [1, 2, 3])],
275
      [(constants.RS_NORMAL, "xyz"), (constants.RS_NORMAL, 1024),
276
       (constants.RS_NORMAL, True), (constants.RS_NORMAL, 4096),
277
       (constants.RS_NORMAL, [{}, {}])],
278
      ])
279

    
280
    self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True),
281
      (cli.QR_NORMAL, [
282
      "Name  Size Active Memory SomeList",
283
      "nodeA  128 N        1.4T []",
284
      "other  512 Y         16M [1, 2, 3]",
285
      "xyz   1024 Y        4.0G [{}, {}]",
286
      ]))
287

    
288
  def testTimestampAndUnit(self):
289
    fields = [
290
      objects.QueryFieldDefinition(name="name", title="Name",
291
                                   kind=constants.QFT_TEXT),
292
      objects.QueryFieldDefinition(name="size", title="Size",
293
                                   kind=constants.QFT_UNIT),
294
      objects.QueryFieldDefinition(name="mtime", title="ModTime",
295
                                   kind=constants.QFT_TIMESTAMP),
296
      ]
297

    
298
    response = objects.QueryResponse(fields=fields, data=[
299
      [(constants.RS_NORMAL, "a"), (constants.RS_NORMAL, 1024),
300
       (constants.RS_NORMAL, 0)],
301
      [(constants.RS_NORMAL, "b"), (constants.RS_NORMAL, 144996),
302
       (constants.RS_NORMAL, 1291746295)],
303
      ])
304

    
305
    self.assertEqual(cli.FormatQueryResult(response, unit="m", header=True),
306
      (cli.QR_NORMAL, [
307
      "Name   Size ModTime",
308
      "a      1024 %s" % utils.FormatTime(0),
309
      "b    144996 %s" % utils.FormatTime(1291746295),
310
      ]))
311

    
312
  def testOverride(self):
313
    fields = [
314
      objects.QueryFieldDefinition(name="name", title="Name",
315
                                   kind=constants.QFT_TEXT),
316
      objects.QueryFieldDefinition(name="cust", title="Custom",
317
                                   kind=constants.QFT_OTHER),
318
      objects.QueryFieldDefinition(name="xt", title="XTime",
319
                                   kind=constants.QFT_TIMESTAMP),
320
      ]
321

    
322
    response = objects.QueryResponse(fields=fields, data=[
323
      [(constants.RS_NORMAL, "x"), (constants.RS_NORMAL, ["a", "b", "c"]),
324
       (constants.RS_NORMAL, 1234)],
325
      [(constants.RS_NORMAL, "y"), (constants.RS_NORMAL, range(10)),
326
       (constants.RS_NORMAL, 1291746295)],
327
      ])
328

    
329
    override = {
330
      "cust": (utils.CommaJoin, False),
331
      "xt": (hex, True),
332
      }
333

    
334
    self.assertEqual(cli.FormatQueryResult(response, unit="h", header=True,
335
                                           format_override=override),
336
      (cli.QR_NORMAL, [
337
      "Name Custom                            XTime",
338
      "x    a, b, c                           0x4d2",
339
      "y    0, 1, 2, 3, 4, 5, 6, 7, 8, 9 0x4cfe7bf7",
340
      ]))
341

    
342
  def testSeparator(self):
343
    fields = [
344
      objects.QueryFieldDefinition(name="name", title="Name",
345
                                   kind=constants.QFT_TEXT),
346
      objects.QueryFieldDefinition(name="count", title="Count",
347
                                   kind=constants.QFT_NUMBER),
348
      objects.QueryFieldDefinition(name="desc", title="Description",
349
                                   kind=constants.QFT_TEXT),
350
      ]
351

    
352
    response = objects.QueryResponse(fields=fields, data=[
353
      [(constants.RS_NORMAL, "instance1.example.com"),
354
       (constants.RS_NORMAL, 21125), (constants.RS_NORMAL, "Hello World!")],
355
      [(constants.RS_NORMAL, "mail.other.net"),
356
       (constants.RS_NORMAL, -9000), (constants.RS_NORMAL, "a,b,c")],
357
      ])
358

    
359
    for sep in [":", "|", "#", "|||", "###", "@@@", "@#@"]:
360
      for header in [None, "Name%sCount%sDescription" % (sep, sep)]:
361
        exp = []
362
        if header:
363
          exp.append(header)
364
        exp.extend([
365
          "instance1.example.com%s21125%sHello World!" % (sep, sep),
366
          "mail.other.net%s-9000%sa,b,c" % (sep, sep),
367
          ])
368

    
369
        self.assertEqual(cli.FormatQueryResult(response, separator=sep,
370
                                               header=bool(header)),
371
                         (cli.QR_NORMAL, exp))
372

    
373
  def testStatusWithUnknown(self):
374
    fields = [
375
      objects.QueryFieldDefinition(name="id", title="ID",
376
                                   kind=constants.QFT_NUMBER),
377
      objects.QueryFieldDefinition(name="unk", title="unk",
378
                                   kind=constants.QFT_UNKNOWN),
379
      objects.QueryFieldDefinition(name="unavail", title="Unavail",
380
                                   kind=constants.QFT_BOOL),
381
      objects.QueryFieldDefinition(name="nodata", title="NoData",
382
                                   kind=constants.QFT_TEXT),
383
      objects.QueryFieldDefinition(name="offline", title="OffLine",
384
                                   kind=constants.QFT_TEXT),
385
      ]
386

    
387
    response = objects.QueryResponse(fields=fields, data=[
388
      [(constants.RS_NORMAL, 1), (constants.RS_UNKNOWN, None),
389
       (constants.RS_NORMAL, False), (constants.RS_NORMAL, ""),
390
       (constants.RS_OFFLINE, None)],
391
      [(constants.RS_NORMAL, 2), (constants.RS_UNKNOWN, None),
392
       (constants.RS_NODATA, None), (constants.RS_NORMAL, "x"),
393
       (constants.RS_OFFLINE, None)],
394
      [(constants.RS_NORMAL, 3), (constants.RS_UNKNOWN, None),
395
       (constants.RS_NORMAL, False), (constants.RS_UNAVAIL, None),
396
       (constants.RS_OFFLINE, None)],
397
      ])
398

    
399
    self.assertEqual(cli.FormatQueryResult(response, header=True,
400
                                           separator="|", verbose=True),
401
      (cli.QR_UNKNOWN, [
402
      "ID|unk|Unavail|NoData|OffLine",
403
      "1|(unknown)|N||(offline)",
404
      "2|(unknown)|(nodata)|x|(offline)",
405
      "3|(unknown)|N|(unavail)|(offline)",
406
      ]))
407
    self.assertEqual(cli.FormatQueryResult(response, header=True,
408
                                           separator="|", verbose=False),
409
      (cli.QR_UNKNOWN, [
410
      "ID|unk|Unavail|NoData|OffLine",
411
      "1|??|N||*",
412
      "2|??|?|x|*",
413
      "3|??|N|-|*",
414
      ]))
415

    
416
  def testNoData(self):
417
    fields = [
418
      objects.QueryFieldDefinition(name="id", title="ID",
419
                                   kind=constants.QFT_NUMBER),
420
      objects.QueryFieldDefinition(name="name", title="Name",
421
                                   kind=constants.QFT_TEXT),
422
      ]
423

    
424
    response = objects.QueryResponse(fields=fields, data=[])
425

    
426
    self.assertEqual(cli.FormatQueryResult(response, header=True),
427
                     (cli.QR_NORMAL, ["ID Name"]))
428

    
429
  def testNoDataWithUnknown(self):
430
    fields = [
431
      objects.QueryFieldDefinition(name="id", title="ID",
432
                                   kind=constants.QFT_NUMBER),
433
      objects.QueryFieldDefinition(name="unk", title="unk",
434
                                   kind=constants.QFT_UNKNOWN),
435
      ]
436

    
437
    response = objects.QueryResponse(fields=fields, data=[])
438

    
439
    self.assertEqual(cli.FormatQueryResult(response, header=False),
440
                     (cli.QR_UNKNOWN, []))
441

    
442
  def testStatus(self):
443
    fields = [
444
      objects.QueryFieldDefinition(name="id", title="ID",
445
                                   kind=constants.QFT_NUMBER),
446
      objects.QueryFieldDefinition(name="unavail", title="Unavail",
447
                                   kind=constants.QFT_BOOL),
448
      objects.QueryFieldDefinition(name="nodata", title="NoData",
449
                                   kind=constants.QFT_TEXT),
450
      objects.QueryFieldDefinition(name="offline", title="OffLine",
451
                                   kind=constants.QFT_TEXT),
452
      ]
453

    
454
    response = objects.QueryResponse(fields=fields, data=[
455
      [(constants.RS_NORMAL, 1), (constants.RS_NORMAL, False),
456
       (constants.RS_NORMAL, ""), (constants.RS_OFFLINE, None)],
457
      [(constants.RS_NORMAL, 2), (constants.RS_NODATA, None),
458
       (constants.RS_NORMAL, "x"), (constants.RS_NORMAL, "abc")],
459
      [(constants.RS_NORMAL, 3), (constants.RS_NORMAL, False),
460
       (constants.RS_UNAVAIL, None), (constants.RS_OFFLINE, None)],
461
      ])
462

    
463
    self.assertEqual(cli.FormatQueryResult(response, header=False,
464
                                           separator="|", verbose=True),
465
      (cli.QR_INCOMPLETE, [
466
      "1|N||(offline)",
467
      "2|(nodata)|x|abc",
468
      "3|N|(unavail)|(offline)",
469
      ]))
470
    self.assertEqual(cli.FormatQueryResult(response, header=False,
471
                                           separator="|", verbose=False),
472
      (cli.QR_INCOMPLETE, [
473
      "1|N||*",
474
      "2|?|x|abc",
475
      "3|N|-|*",
476
      ]))
477

    
478
  def testInvalidFieldType(self):
479
    fields = [
480
      objects.QueryFieldDefinition(name="x", title="x",
481
                                   kind="#some#other#type"),
482
      ]
483

    
484
    response = objects.QueryResponse(fields=fields, data=[])
485

    
486
    self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
487

    
488
  def testInvalidFieldStatus(self):
489
    fields = [
490
      objects.QueryFieldDefinition(name="x", title="x",
491
                                   kind=constants.QFT_TEXT),
492
      ]
493

    
494
    response = objects.QueryResponse(fields=fields, data=[[(-1, None)]])
495
    self.assertRaises(NotImplementedError, cli.FormatQueryResult, response)
496

    
497
    response = objects.QueryResponse(fields=fields, data=[[(-1, "x")]])
498
    self.assertRaises(AssertionError, cli.FormatQueryResult, response)
499

    
500
  def testEmptyFieldTitle(self):
501
    fields = [
502
      objects.QueryFieldDefinition(name="x", title="",
503
                                   kind=constants.QFT_TEXT),
504
      ]
505

    
506
    response = objects.QueryResponse(fields=fields, data=[])
507
    self.assertRaises(AssertionError, cli.FormatQueryResult, response)
508

    
509

    
510
class _MockJobPollCb(cli.JobPollCbBase, cli.JobPollReportCbBase):
511
  def __init__(self, tc, job_id):
512
    self.tc = tc
513
    self.job_id = job_id
514
    self._wfjcr = []
515
    self._jobstatus = []
516
    self._expect_notchanged = False
517
    self._expect_log = []
518

    
519
  def CheckEmpty(self):
520
    self.tc.assertFalse(self._wfjcr)
521
    self.tc.assertFalse(self._jobstatus)
522
    self.tc.assertFalse(self._expect_notchanged)
523
    self.tc.assertFalse(self._expect_log)
524

    
525
  def AddWfjcResult(self, *args):
526
    self._wfjcr.append(args)
527

    
528
  def AddQueryJobsResult(self, *args):
529
    self._jobstatus.append(args)
530

    
531
  def WaitForJobChangeOnce(self, job_id, fields,
532
                           prev_job_info, prev_log_serial):
533
    self.tc.assertEqual(job_id, self.job_id)
534
    self.tc.assertEqualValues(fields, ["status"])
535
    self.tc.assertFalse(self._expect_notchanged)
536
    self.tc.assertFalse(self._expect_log)
537

    
538
    (exp_prev_job_info, exp_prev_log_serial, result) = self._wfjcr.pop(0)
539
    self.tc.assertEqualValues(prev_job_info, exp_prev_job_info)
540
    self.tc.assertEqual(prev_log_serial, exp_prev_log_serial)
541

    
542
    if result == constants.JOB_NOTCHANGED:
543
      self._expect_notchanged = True
544
    elif result:
545
      (_, logmsgs) = result
546
      if logmsgs:
547
        self._expect_log.extend(logmsgs)
548

    
549
    return result
550

    
551
  def QueryJobs(self, job_ids, fields):
552
    self.tc.assertEqual(job_ids, [self.job_id])
553
    self.tc.assertEqualValues(fields, ["status", "opstatus", "opresult"])
554
    self.tc.assertFalse(self._expect_notchanged)
555
    self.tc.assertFalse(self._expect_log)
556

    
557
    result = self._jobstatus.pop(0)
558
    self.tc.assertEqual(len(fields), len(result))
559
    return [result]
560

    
561
  def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
562
    self.tc.assertEqual(job_id, self.job_id)
563
    self.tc.assertEqualValues((serial, timestamp, log_type, log_msg),
564
                              self._expect_log.pop(0))
565

    
566
  def ReportNotChanged(self, job_id, status):
567
    self.tc.assertEqual(job_id, self.job_id)
568
    self.tc.assert_(self._expect_notchanged)
569
    self._expect_notchanged = False
570

    
571

    
572
class TestGenericPollJob(testutils.GanetiTestCase):
573
  def testSuccessWithLog(self):
574
    job_id = 29609
575
    cbs = _MockJobPollCb(self, job_id)
576

    
577
    cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
578

    
579
    cbs.AddWfjcResult(None, None,
580
                      ((constants.JOB_STATUS_QUEUED, ), None))
581

    
582
    cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
583
                      constants.JOB_NOTCHANGED)
584

    
585
    cbs.AddWfjcResult((constants.JOB_STATUS_QUEUED, ), None,
586
                      ((constants.JOB_STATUS_RUNNING, ),
587
                       [(1, utils.SplitTime(1273491611.0),
588
                         constants.ELOG_MESSAGE, "Step 1"),
589
                        (2, utils.SplitTime(1273491615.9),
590
                         constants.ELOG_MESSAGE, "Step 2"),
591
                        (3, utils.SplitTime(1273491625.02),
592
                         constants.ELOG_MESSAGE, "Step 3"),
593
                        (4, utils.SplitTime(1273491635.05),
594
                         constants.ELOG_MESSAGE, "Step 4"),
595
                        (37, utils.SplitTime(1273491645.0),
596
                         constants.ELOG_MESSAGE, "Step 5"),
597
                        (203, utils.SplitTime(127349155.0),
598
                         constants.ELOG_MESSAGE, "Step 6")]))
599

    
600
    cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 203,
601
                      ((constants.JOB_STATUS_RUNNING, ),
602
                       [(300, utils.SplitTime(1273491711.01),
603
                         constants.ELOG_MESSAGE, "Step X"),
604
                        (302, utils.SplitTime(1273491815.8),
605
                         constants.ELOG_MESSAGE, "Step Y"),
606
                        (303, utils.SplitTime(1273491925.32),
607
                         constants.ELOG_MESSAGE, "Step Z")]))
608

    
609
    cbs.AddWfjcResult((constants.JOB_STATUS_RUNNING, ), 303,
610
                      ((constants.JOB_STATUS_SUCCESS, ), None))
611

    
612
    cbs.AddQueryJobsResult(constants.JOB_STATUS_SUCCESS,
613
                           [constants.OP_STATUS_SUCCESS,
614
                            constants.OP_STATUS_SUCCESS],
615
                           ["Hello World", "Foo man bar"])
616

    
617
    self.assertEqual(["Hello World", "Foo man bar"],
618
                     cli.GenericPollJob(job_id, cbs, cbs))
619
    cbs.CheckEmpty()
620

    
621
  def testJobLost(self):
622
    job_id = 13746
623

    
624
    cbs = _MockJobPollCb(self, job_id)
625
    cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
626
    cbs.AddWfjcResult(None, None, None)
627
    self.assertRaises(errors.JobLost, cli.GenericPollJob, job_id, cbs, cbs)
628
    cbs.CheckEmpty()
629

    
630
  def testError(self):
631
    job_id = 31088
632

    
633
    cbs = _MockJobPollCb(self, job_id)
634
    cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
635
    cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
636
    cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
637
                           [constants.OP_STATUS_SUCCESS,
638
                            constants.OP_STATUS_ERROR],
639
                           ["Hello World", "Error code 123"])
640
    self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
641
    cbs.CheckEmpty()
642

    
643
  def testError2(self):
644
    job_id = 22235
645

    
646
    cbs = _MockJobPollCb(self, job_id)
647
    cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
648
    encexc = errors.EncodeException(errors.LockError("problem"))
649
    cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
650
                           [constants.OP_STATUS_ERROR], [encexc])
651
    self.assertRaises(errors.LockError, cli.GenericPollJob, job_id, cbs, cbs)
652
    cbs.CheckEmpty()
653

    
654
  def testWeirdError(self):
655
    job_id = 28847
656

    
657
    cbs = _MockJobPollCb(self, job_id)
658
    cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_ERROR, ), None))
659
    cbs.AddQueryJobsResult(constants.JOB_STATUS_ERROR,
660
                           [constants.OP_STATUS_RUNNING,
661
                            constants.OP_STATUS_RUNNING],
662
                           [None, None])
663
    self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
664
    cbs.CheckEmpty()
665

    
666
  def testCancel(self):
667
    job_id = 4275
668

    
669
    cbs = _MockJobPollCb(self, job_id)
670
    cbs.AddWfjcResult(None, None, constants.JOB_NOTCHANGED)
671
    cbs.AddWfjcResult(None, None, ((constants.JOB_STATUS_CANCELING, ), None))
672
    cbs.AddQueryJobsResult(constants.JOB_STATUS_CANCELING,
673
                           [constants.OP_STATUS_CANCELING,
674
                            constants.OP_STATUS_CANCELING],
675
                           [None, None])
676
    self.assertRaises(errors.OpExecError, cli.GenericPollJob, job_id, cbs, cbs)
677
    cbs.CheckEmpty()
678

    
679

    
680
class TestFormatLogMessage(unittest.TestCase):
681
  def test(self):
682
    self.assertEqual(cli.FormatLogMessage(constants.ELOG_MESSAGE,
683
                                          "Hello World"),
684
                     "Hello World")
685
    self.assertRaises(TypeError, cli.FormatLogMessage,
686
                      constants.ELOG_MESSAGE, [1, 2, 3])
687

    
688
    self.assert_(cli.FormatLogMessage("some other type", (1, 2, 3)))
689

    
690

    
691
class TestParseFields(unittest.TestCase):
692
  def test(self):
693
    self.assertEqual(cli.ParseFields(None, []), [])
694
    self.assertEqual(cli.ParseFields("name,foo,hello", []),
695
                     ["name", "foo", "hello"])
696
    self.assertEqual(cli.ParseFields(None, ["def", "ault", "fields", "here"]),
697
                     ["def", "ault", "fields", "here"])
698
    self.assertEqual(cli.ParseFields("name,foo", ["def", "ault"]),
699
                     ["name", "foo"])
700
    self.assertEqual(cli.ParseFields("+name,foo", ["def", "ault"]),
701
                     ["def", "ault", "name", "foo"])
702

    
703

    
704
class TestConstants(unittest.TestCase):
705
  def testPriority(self):
706
    self.assertEqual(set(cli._PRIONAME_TO_VALUE.values()),
707
                     set(constants.OP_PRIO_SUBMIT_VALID))
708
    self.assertEqual(list(value for _, value in cli._PRIORITY_NAMES),
709
                     sorted(constants.OP_PRIO_SUBMIT_VALID, reverse=True))
710

    
711

    
712
class TestParseNicOption(unittest.TestCase):
713
  def test(self):
714
    self.assertEqual(cli.ParseNicOption([("0", { "link": "eth0", })]),
715
                     [{ "link": "eth0", }])
716
    self.assertEqual(cli.ParseNicOption([("5", { "ip": "192.0.2.7", })]),
717
                     [{}, {}, {}, {}, {}, { "ip": "192.0.2.7", }])
718

    
719
  def testErrors(self):
720
    for i in [None, "", "abc", "zero", "Hello World", "\0", []]:
721
      self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
722
                        [(i, { "link": "eth0", })])
723
      self.assertRaises(errors.OpPrereqError, cli.ParseNicOption,
724
                        [("0", i)])
725

    
726
    self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
727
                      [(0, { True: False, })])
728

    
729
    self.assertRaises(errors.TypeEnforcementError, cli.ParseNicOption,
730
                      [(3, { "mode": [], })])
731

    
732

    
733
class TestFormatResultError(unittest.TestCase):
734
  def testNormal(self):
735
    for verbose in [False, True]:
736
      self.assertRaises(AssertionError, cli.FormatResultError,
737
                        constants.RS_NORMAL, verbose)
738

    
739
  def testUnknown(self):
740
    for verbose in [False, True]:
741
      self.assertRaises(NotImplementedError, cli.FormatResultError,
742
                        "#some!other!status#", verbose)
743

    
744
  def test(self):
745
    for status in constants.RS_ALL:
746
      if status == constants.RS_NORMAL:
747
        continue
748

    
749
      self.assertNotEqual(cli.FormatResultError(status, False),
750
                          cli.FormatResultError(status, True))
751

    
752
      result = cli.FormatResultError(status, True)
753
      self.assertTrue(result.startswith("("))
754
      self.assertTrue(result.endswith(")"))
755

    
756

    
757
class TestGetOnlineNodes(unittest.TestCase):
758
  class _FakeClient:
759
    def __init__(self):
760
      self._query = []
761

    
762
    def AddQueryResult(self, *args):
763
      self._query.append(args)
764

    
765
    def CountPending(self):
766
      return len(self._query)
767

    
768
    def Query(self, res, fields, filter_):
769
      if res != constants.QR_NODE:
770
        raise Exception("Querying wrong resource")
771

    
772
      (exp_fields, check_filter, result) = self._query.pop(0)
773

    
774
      if exp_fields != fields:
775
        raise Exception("Expected fields %s, got %s" % (exp_fields, fields))
776

    
777
      if not (filter_ is None or check_filter(filter_)):
778
        raise Exception("Filter doesn't match expectations")
779

    
780
      return objects.QueryResponse(fields=None, data=result)
781

    
782
  def testEmpty(self):
783
    cl = self._FakeClient()
784

    
785
    cl.AddQueryResult(["name", "offline", "sip"], None, [])
786
    self.assertEqual(cli.GetOnlineNodes(None, cl=cl), [])
787
    self.assertEqual(cl.CountPending(), 0)
788

    
789
  def testNoSpecialFilter(self):
790
    cl = self._FakeClient()
791

    
792
    cl.AddQueryResult(["name", "offline", "sip"], None, [
793
      [(constants.RS_NORMAL, "master.example.com"),
794
       (constants.RS_NORMAL, False),
795
       (constants.RS_NORMAL, "192.0.2.1")],
796
      [(constants.RS_NORMAL, "node2.example.com"),
797
       (constants.RS_NORMAL, False),
798
       (constants.RS_NORMAL, "192.0.2.2")],
799
      ])
800
    self.assertEqual(cli.GetOnlineNodes(None, cl=cl),
801
                     ["master.example.com", "node2.example.com"])
802
    self.assertEqual(cl.CountPending(), 0)
803

    
804
  def testNoMaster(self):
805
    cl = self._FakeClient()
806

    
807
    def _CheckFilter(filter_):
808
      self.assertEqual(filter_, [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
809
      return True
810

    
811
    cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
812
      [(constants.RS_NORMAL, "node2.example.com"),
813
       (constants.RS_NORMAL, False),
814
       (constants.RS_NORMAL, "192.0.2.2")],
815
      ])
816
    self.assertEqual(cli.GetOnlineNodes(None, cl=cl, filter_master=True),
817
                     ["node2.example.com"])
818
    self.assertEqual(cl.CountPending(), 0)
819

    
820
  def testSecondaryIpAddress(self):
821
    cl = self._FakeClient()
822

    
823
    cl.AddQueryResult(["name", "offline", "sip"], None, [
824
      [(constants.RS_NORMAL, "master.example.com"),
825
       (constants.RS_NORMAL, False),
826
       (constants.RS_NORMAL, "192.0.2.1")],
827
      [(constants.RS_NORMAL, "node2.example.com"),
828
       (constants.RS_NORMAL, False),
829
       (constants.RS_NORMAL, "192.0.2.2")],
830
      ])
831
    self.assertEqual(cli.GetOnlineNodes(None, cl=cl, secondary_ips=True),
832
                     ["192.0.2.1", "192.0.2.2"])
833
    self.assertEqual(cl.CountPending(), 0)
834

    
835
  def testNoMasterFilterNodeName(self):
836
    cl = self._FakeClient()
837

    
838
    def _CheckFilter(filter_):
839
      self.assertEqual(filter_,
840
        [qlang.OP_AND,
841
         [qlang.OP_OR] + [[qlang.OP_EQUAL, "name", name]
842
                          for name in ["node2", "node3"]],
843
         [qlang.OP_NOT, [qlang.OP_TRUE, "master"]]])
844
      return True
845

    
846
    cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
847
      [(constants.RS_NORMAL, "node2.example.com"),
848
       (constants.RS_NORMAL, False),
849
       (constants.RS_NORMAL, "192.0.2.12")],
850
      [(constants.RS_NORMAL, "node3.example.com"),
851
       (constants.RS_NORMAL, False),
852
       (constants.RS_NORMAL, "192.0.2.13")],
853
      ])
854
    self.assertEqual(cli.GetOnlineNodes(["node2", "node3"], cl=cl,
855
                                        secondary_ips=True, filter_master=True),
856
                     ["192.0.2.12", "192.0.2.13"])
857
    self.assertEqual(cl.CountPending(), 0)
858

    
859
  def testOfflineNodes(self):
860
    cl = self._FakeClient()
861

    
862
    cl.AddQueryResult(["name", "offline", "sip"], None, [
863
      [(constants.RS_NORMAL, "master.example.com"),
864
       (constants.RS_NORMAL, False),
865
       (constants.RS_NORMAL, "192.0.2.1")],
866
      [(constants.RS_NORMAL, "node2.example.com"),
867
       (constants.RS_NORMAL, True),
868
       (constants.RS_NORMAL, "192.0.2.2")],
869
      [(constants.RS_NORMAL, "node3.example.com"),
870
       (constants.RS_NORMAL, True),
871
       (constants.RS_NORMAL, "192.0.2.3")],
872
      ])
873
    self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nowarn=True),
874
                     ["master.example.com"])
875
    self.assertEqual(cl.CountPending(), 0)
876

    
877
  def testNodeGroup(self):
878
    cl = self._FakeClient()
879

    
880
    def _CheckFilter(filter_):
881
      self.assertEqual(filter_,
882
        [qlang.OP_OR, [qlang.OP_EQUAL, "group", "foobar"],
883
                      [qlang.OP_EQUAL, "group.uuid", "foobar"]])
884
      return True
885

    
886
    cl.AddQueryResult(["name", "offline", "sip"], _CheckFilter, [
887
      [(constants.RS_NORMAL, "master.example.com"),
888
       (constants.RS_NORMAL, False),
889
       (constants.RS_NORMAL, "192.0.2.1")],
890
      [(constants.RS_NORMAL, "node3.example.com"),
891
       (constants.RS_NORMAL, False),
892
       (constants.RS_NORMAL, "192.0.2.3")],
893
      ])
894
    self.assertEqual(cli.GetOnlineNodes(None, cl=cl, nodegroup="foobar"),
895
                     ["master.example.com", "node3.example.com"])
896
    self.assertEqual(cl.CountPending(), 0)
897

    
898

    
899
if __name__ == '__main__':
900
  testutils.GanetiTestProgram()