Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 8572f1fe

History | View | Annotate | Download (39.9 kB)

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

    
4
# Copyright (C) 2010 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 RAPI client module"""
23

    
24

    
25
import re
26
import unittest
27
import warnings
28
import pycurl
29

    
30
from ganeti import constants
31
from ganeti import http
32
from ganeti import serializer
33

    
34
from ganeti.rapi import connector
35
from ganeti.rapi import rlib2
36
from ganeti.rapi import client
37

    
38
import testutils
39

    
40

    
41
_URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
42

    
43

    
44
def _GetPathFromUri(uri):
45
  """Gets the path and query from a URI.
46

47
  """
48
  match = _URI_RE.match(uri)
49
  if match:
50
    return match.groupdict()["path"]
51
  else:
52
    return None
53

    
54

    
55
class FakeCurl:
56
  def __init__(self, rapi):
57
    self._rapi = rapi
58
    self._opts = {}
59
    self._info = {}
60

    
61
  def setopt(self, opt, value):
62
    self._opts[opt] = value
63

    
64
  def getopt(self, opt):
65
    return self._opts.get(opt)
66

    
67
  def unsetopt(self, opt):
68
    self._opts.pop(opt, None)
69

    
70
  def getinfo(self, info):
71
    return self._info[info]
72

    
73
  def perform(self):
74
    method = self._opts[pycurl.CUSTOMREQUEST]
75
    url = self._opts[pycurl.URL]
76
    request_body = self._opts[pycurl.POSTFIELDS]
77
    writefn = self._opts[pycurl.WRITEFUNCTION]
78

    
79
    path = _GetPathFromUri(url)
80
    (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
81

    
82
    self._info[pycurl.RESPONSE_CODE] = code
83
    if resp_body is not None:
84
      writefn(resp_body)
85

    
86

    
87
class RapiMock(object):
88
  def __init__(self):
89
    self._mapper = connector.Mapper()
90
    self._responses = []
91
    self._last_handler = None
92
    self._last_req_data = None
93

    
94
  def AddResponse(self, response, code=200):
95
    self._responses.insert(0, (code, response))
96

    
97
  def CountPending(self):
98
    return len(self._responses)
99

    
100
  def GetLastHandler(self):
101
    return self._last_handler
102

    
103
  def GetLastRequestData(self):
104
    return self._last_req_data
105

    
106
  def FetchResponse(self, path, method, request_body):
107
    self._last_req_data = request_body
108

    
109
    try:
110
      HandlerClass, items, args = self._mapper.getController(path)
111
      self._last_handler = HandlerClass(items, args, None)
112
      if not hasattr(self._last_handler, method.upper()):
113
        raise http.HttpNotImplemented(message="Method not implemented")
114

    
115
    except http.HttpException, ex:
116
      code = ex.code
117
      response = ex.message
118
    else:
119
      if not self._responses:
120
        raise Exception("No responses")
121

    
122
      (code, response) = self._responses.pop()
123

    
124
    return code, response
125

    
126

    
127
class TestConstants(unittest.TestCase):
128
  def test(self):
129
    self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
130
    self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
131
    self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
132
    self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
133
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
134
    self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
135
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
136

    
137

    
138
class RapiMockTest(unittest.TestCase):
139
  def test(self):
140
    rapi = RapiMock()
141
    path = "/version"
142
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
143
    self.assertEqual((501, "Method not implemented"),
144
                     rapi.FetchResponse("/version", "POST", None))
145
    rapi.AddResponse("2")
146
    code, response = rapi.FetchResponse("/version", "GET", None)
147
    self.assertEqual(200, code)
148
    self.assertEqual("2", response)
149
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
150

    
151

    
152
def _FakeNoSslPycurlVersion():
153
  # Note: incomplete version tuple
154
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
155

    
156

    
157
def _FakeFancySslPycurlVersion():
158
  # Note: incomplete version tuple
159
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
160

    
161

    
162
def _FakeOpenSslPycurlVersion():
163
  # Note: incomplete version tuple
164
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
165

    
166

    
167
def _FakeGnuTlsPycurlVersion():
168
  # Note: incomplete version tuple
169
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
170

    
171

    
172
class TestExtendedConfig(unittest.TestCase):
173
  def testAuth(self):
174
    cl = client.GanetiRapiClient("master.example.com",
175
                                 username="user", password="pw",
176
                                 curl_factory=lambda: FakeCurl(RapiMock()))
177

    
178
    curl = cl._CreateCurl()
179
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
180
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
181

    
182
  def testInvalidAuth(self):
183
    # No username
184
    self.assertRaises(client.Error, client.GanetiRapiClient,
185
                      "master-a.example.com", password="pw")
186
    # No password
187
    self.assertRaises(client.Error, client.GanetiRapiClient,
188
                      "master-b.example.com", username="user")
189

    
190
  def testCertVerifyInvalidCombinations(self):
191
    self.assertRaises(client.Error, client.GenericCurlConfig,
192
                      use_curl_cabundle=True, cafile="cert1.pem")
193
    self.assertRaises(client.Error, client.GenericCurlConfig,
194
                      use_curl_cabundle=True, capath="certs/")
195
    self.assertRaises(client.Error, client.GenericCurlConfig,
196
                      use_curl_cabundle=True,
197
                      cafile="cert1.pem", capath="certs/")
198

    
199
  def testProxySignalVerifyHostname(self):
200
    for use_gnutls in [False, True]:
201
      if use_gnutls:
202
        pcverfn = _FakeGnuTlsPycurlVersion
203
      else:
204
        pcverfn = _FakeOpenSslPycurlVersion
205

    
206
      for proxy in ["", "http://127.0.0.1:1234"]:
207
        for use_signal in [False, True]:
208
          for verify_hostname in [False, True]:
209
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
210
                                             verify_hostname=verify_hostname,
211
                                             _pycurl_version_fn=pcverfn)
212

    
213
            curl_factory = lambda: FakeCurl(RapiMock())
214
            cl = client.GanetiRapiClient("master.example.com",
215
                                         curl_config_fn=cfgfn,
216
                                         curl_factory=curl_factory)
217

    
218
            curl = cl._CreateCurl()
219
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
220
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
221

    
222
            if verify_hostname:
223
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
224
            else:
225
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
226

    
227
  def testNoCertVerify(self):
228
    cfgfn = client.GenericCurlConfig()
229

    
230
    curl_factory = lambda: FakeCurl(RapiMock())
231
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
232
                                 curl_factory=curl_factory)
233

    
234
    curl = cl._CreateCurl()
235
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
236
    self.assertFalse(curl.getopt(pycurl.CAINFO))
237
    self.assertFalse(curl.getopt(pycurl.CAPATH))
238

    
239
  def testCertVerifyCurlBundle(self):
240
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
241

    
242
    curl_factory = lambda: FakeCurl(RapiMock())
243
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
244
                                 curl_factory=curl_factory)
245

    
246
    curl = cl._CreateCurl()
247
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
248
    self.assertFalse(curl.getopt(pycurl.CAINFO))
249
    self.assertFalse(curl.getopt(pycurl.CAPATH))
250

    
251
  def testCertVerifyCafile(self):
252
    mycert = "/tmp/some/UNUSED/cert/file.pem"
253
    cfgfn = client.GenericCurlConfig(cafile=mycert)
254

    
255
    curl_factory = lambda: FakeCurl(RapiMock())
256
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
257
                                 curl_factory=curl_factory)
258

    
259
    curl = cl._CreateCurl()
260
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
261
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
262
    self.assertFalse(curl.getopt(pycurl.CAPATH))
263

    
264
  def testCertVerifyCapath(self):
265
    certdir = "/tmp/some/UNUSED/cert/directory"
266
    pcverfn = _FakeOpenSslPycurlVersion
267
    cfgfn = client.GenericCurlConfig(capath=certdir,
268
                                     _pycurl_version_fn=pcverfn)
269

    
270
    curl_factory = lambda: FakeCurl(RapiMock())
271
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
272
                                 curl_factory=curl_factory)
273

    
274
    curl = cl._CreateCurl()
275
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
276
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
277
    self.assertFalse(curl.getopt(pycurl.CAINFO))
278

    
279
  def testCertVerifyCapathGnuTls(self):
280
    certdir = "/tmp/some/UNUSED/cert/directory"
281
    pcverfn = _FakeGnuTlsPycurlVersion
282
    cfgfn = client.GenericCurlConfig(capath=certdir,
283
                                     _pycurl_version_fn=pcverfn)
284

    
285
    curl_factory = lambda: FakeCurl(RapiMock())
286
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
287
                                 curl_factory=curl_factory)
288

    
289
    self.assertRaises(client.Error, cl._CreateCurl)
290

    
291
  def testCertVerifyNoSsl(self):
292
    certdir = "/tmp/some/UNUSED/cert/directory"
293
    pcverfn = _FakeNoSslPycurlVersion
294
    cfgfn = client.GenericCurlConfig(capath=certdir,
295
                                     _pycurl_version_fn=pcverfn)
296

    
297
    curl_factory = lambda: FakeCurl(RapiMock())
298
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
299
                                 curl_factory=curl_factory)
300

    
301
    self.assertRaises(client.Error, cl._CreateCurl)
302

    
303
  def testCertVerifyFancySsl(self):
304
    certdir = "/tmp/some/UNUSED/cert/directory"
305
    pcverfn = _FakeFancySslPycurlVersion
306
    cfgfn = client.GenericCurlConfig(capath=certdir,
307
                                     _pycurl_version_fn=pcverfn)
308

    
309
    curl_factory = lambda: FakeCurl(RapiMock())
310
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
311
                                 curl_factory=curl_factory)
312

    
313
    self.assertRaises(NotImplementedError, cl._CreateCurl)
314

    
315
  def testCertVerifyCapath(self):
316
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
317
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
318
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
319
                                         timeout=timeout)
320

    
321
        curl_factory = lambda: FakeCurl(RapiMock())
322
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
323
                                     curl_factory=curl_factory)
324

    
325
        curl = cl._CreateCurl()
326
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
327
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
328

    
329

    
330
class GanetiRapiClientTests(testutils.GanetiTestCase):
331
  def setUp(self):
332
    testutils.GanetiTestCase.setUp(self)
333

    
334
    self.rapi = RapiMock()
335
    self.curl = FakeCurl(self.rapi)
336
    self.client = client.GanetiRapiClient("master.example.com",
337
                                          curl_factory=lambda: self.curl)
338

    
339
  def assertHandler(self, handler_cls):
340
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
341

    
342
  def assertQuery(self, key, value):
343
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
344

    
345
  def assertItems(self, items):
346
    self.assertEqual(items, self.rapi.GetLastHandler().items)
347

    
348
  def assertBulk(self):
349
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
350

    
351
  def assertDryRun(self):
352
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
353

    
354
  def assertUseForce(self):
355
    self.assertTrue(self.rapi.GetLastHandler().useForce())
356

    
357
  def testEncodeQuery(self):
358
    query = [
359
      ("a", None),
360
      ("b", 1),
361
      ("c", 2),
362
      ("d", "Foo"),
363
      ("e", True),
364
      ]
365

    
366
    expected = [
367
      ("a", ""),
368
      ("b", 1),
369
      ("c", 2),
370
      ("d", "Foo"),
371
      ("e", 1),
372
      ]
373

    
374
    self.assertEqualValues(self.client._EncodeQuery(query),
375
                           expected)
376

    
377
    # invalid types
378
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
379
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
380

    
381
  def testCurlSettings(self):
382
    self.rapi.AddResponse("2")
383
    self.assertEqual(2, self.client.GetVersion())
384
    self.assertHandler(rlib2.R_version)
385

    
386
    # Signals should be disabled by default
387
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
388

    
389
    # No auth and no proxy
390
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
391
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
392

    
393
    # Content-type is required for requests
394
    headers = self.curl.getopt(pycurl.HTTPHEADER)
395
    self.assert_("Content-type: application/json" in headers)
396

    
397
  def testHttpError(self):
398
    self.rapi.AddResponse(None, code=404)
399
    try:
400
      self.client.GetJobStatus(15140)
401
    except client.GanetiApiError, err:
402
      self.assertEqual(err.code, 404)
403
    else:
404
      self.fail("Didn't raise exception")
405

    
406
  def testGetVersion(self):
407
    self.rapi.AddResponse("2")
408
    self.assertEqual(2, self.client.GetVersion())
409
    self.assertHandler(rlib2.R_version)
410

    
411
  def testGetFeatures(self):
412
    for features in [[], ["foo", "bar", "baz"]]:
413
      self.rapi.AddResponse(serializer.DumpJson(features))
414
      self.assertEqual(features, self.client.GetFeatures())
415
      self.assertHandler(rlib2.R_2_features)
416

    
417
  def testGetFeaturesNotFound(self):
418
    self.rapi.AddResponse(None, code=404)
419
    self.assertEqual([], self.client.GetFeatures())
420

    
421
  def testGetOperatingSystems(self):
422
    self.rapi.AddResponse("[\"beos\"]")
423
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
424
    self.assertHandler(rlib2.R_2_os)
425

    
426
  def testGetClusterTags(self):
427
    self.rapi.AddResponse("[\"tag\"]")
428
    self.assertEqual(["tag"], self.client.GetClusterTags())
429
    self.assertHandler(rlib2.R_2_tags)
430

    
431
  def testAddClusterTags(self):
432
    self.rapi.AddResponse("1234")
433
    self.assertEqual(1234,
434
        self.client.AddClusterTags(["awesome"], dry_run=True))
435
    self.assertHandler(rlib2.R_2_tags)
436
    self.assertDryRun()
437
    self.assertQuery("tag", ["awesome"])
438

    
439
  def testDeleteClusterTags(self):
440
    self.rapi.AddResponse("5107")
441
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
442
                                                         dry_run=True))
443
    self.assertHandler(rlib2.R_2_tags)
444
    self.assertDryRun()
445
    self.assertQuery("tag", ["awesome"])
446

    
447
  def testGetInfo(self):
448
    self.rapi.AddResponse("{}")
449
    self.assertEqual({}, self.client.GetInfo())
450
    self.assertHandler(rlib2.R_2_info)
451

    
452
  def testGetInstances(self):
453
    self.rapi.AddResponse("[]")
454
    self.assertEqual([], self.client.GetInstances(bulk=True))
455
    self.assertHandler(rlib2.R_2_instances)
456
    self.assertBulk()
457

    
458
  def testGetInstance(self):
459
    self.rapi.AddResponse("[]")
460
    self.assertEqual([], self.client.GetInstance("instance"))
461
    self.assertHandler(rlib2.R_2_instances_name)
462
    self.assertItems(["instance"])
463

    
464
  def testGetInstanceInfo(self):
465
    self.rapi.AddResponse("21291")
466
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
467
    self.assertHandler(rlib2.R_2_instances_name_info)
468
    self.assertItems(["inst3"])
469
    self.assertQuery("static", None)
470

    
471
    self.rapi.AddResponse("3428")
472
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
473
    self.assertHandler(rlib2.R_2_instances_name_info)
474
    self.assertItems(["inst31"])
475
    self.assertQuery("static", ["0"])
476

    
477
    self.rapi.AddResponse("15665")
478
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
479
    self.assertHandler(rlib2.R_2_instances_name_info)
480
    self.assertItems(["inst32"])
481
    self.assertQuery("static", ["1"])
482

    
483
  def testCreateInstanceOldVersion(self):
484
    # No NICs
485
    self.rapi.AddResponse(None, code=404)
486
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
487
                      "create", "inst1.example.com", "plain", [], [])
488
    self.assertEqual(self.rapi.CountPending(), 0)
489

    
490
    # More than one NIC
491
    self.rapi.AddResponse(None, code=404)
492
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
493
                      "create", "inst1.example.com", "plain", [],
494
                      [{}, {}, {}])
495
    self.assertEqual(self.rapi.CountPending(), 0)
496

    
497
    # Unsupported NIC fields
498
    self.rapi.AddResponse(None, code=404)
499
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
500
                      "create", "inst1.example.com", "plain", [],
501
                      [{"x": True, "y": False}])
502
    self.assertEqual(self.rapi.CountPending(), 0)
503

    
504
    # Unsupported disk fields
505
    self.rapi.AddResponse(None, code=404)
506
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
507
                      "create", "inst1.example.com", "plain",
508
                      [{}, {"moo": "foo",}], [{}])
509
    self.assertEqual(self.rapi.CountPending(), 0)
510

    
511
    # Unsupported fields
512
    self.rapi.AddResponse(None, code=404)
513
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
514
                      "create", "inst1.example.com", "plain", [], [{}],
515
                      hello_world=123)
516
    self.assertEqual(self.rapi.CountPending(), 0)
517

    
518
    self.rapi.AddResponse(None, code=404)
519
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
520
                      "create", "inst1.example.com", "plain", [], [{}],
521
                      memory=128)
522
    self.assertEqual(self.rapi.CountPending(), 0)
523

    
524
    # Normal creation
525
    testnics = [
526
      [{}],
527
      [{ "mac": constants.VALUE_AUTO, }],
528
      [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
529
      ]
530

    
531
    testdisks = [
532
      [],
533
      [{ "size": 128, }],
534
      [{ "size": 321, }, { "size": 4096, }],
535
      ]
536

    
537
    for idx, nics in enumerate(testnics):
538
      for disks in testdisks:
539
        beparams = {
540
          constants.BE_MEMORY: 512,
541
          constants.BE_AUTO_BALANCE: False,
542
          }
543
        hvparams = {
544
          constants.HV_MIGRATION_PORT: 9876,
545
          constants.HV_VNC_TLS: True,
546
          }
547

    
548
        self.rapi.AddResponse(None, code=404)
549
        self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
550
        job_id = self.client.CreateInstance("create", "inst1.example.com",
551
                                            "plain", disks, nics,
552
                                            pnode="node99", dry_run=True,
553
                                            hvparams=hvparams,
554
                                            beparams=beparams)
555
        self.assertEqual(job_id, 3122617 + idx)
556
        self.assertHandler(rlib2.R_2_instances)
557
        self.assertDryRun()
558
        self.assertEqual(self.rapi.CountPending(), 0)
559

    
560
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
561
        self.assertEqual(data["name"], "inst1.example.com")
562
        self.assertEqual(data["disk_template"], "plain")
563
        self.assertEqual(data["pnode"], "node99")
564
        self.assertEqual(data[constants.BE_MEMORY], 512)
565
        self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
566
        self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
567
        self.assertEqual(data[constants.HV_VNC_TLS], True)
568
        self.assertEqual(data["disks"], [disk["size"] for disk in disks])
569

    
570
  def testCreateInstance(self):
571
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
572
    self.rapi.AddResponse("23030")
573
    job_id = self.client.CreateInstance("create", "inst1.example.com",
574
                                        "plain", [], [], dry_run=True)
575
    self.assertEqual(job_id, 23030)
576
    self.assertHandler(rlib2.R_2_instances)
577
    self.assertDryRun()
578

    
579
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
580

    
581
    for field in ["dry_run", "beparams", "hvparams", "start"]:
582
      self.assertFalse(field in data)
583

    
584
    self.assertEqual(data["name"], "inst1.example.com")
585
    self.assertEqual(data["disk_template"], "plain")
586

    
587
  def testCreateInstance2(self):
588
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
589
    self.rapi.AddResponse("24740")
590
    job_id = self.client.CreateInstance("import", "inst2.example.com",
591
                                        "drbd8", [{"size": 100,}],
592
                                        [{}, {"bridge": "br1", }],
593
                                        dry_run=False, start=True,
594
                                        pnode="node1", snode="node9",
595
                                        ip_check=False)
596
    self.assertEqual(job_id, 24740)
597
    self.assertHandler(rlib2.R_2_instances)
598

    
599
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
600
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
601
    self.assertEqual(data["name"], "inst2.example.com")
602
    self.assertEqual(data["disk_template"], "drbd8")
603
    self.assertEqual(data["start"], True)
604
    self.assertEqual(data["ip_check"], False)
605
    self.assertEqualValues(data["disks"], [{"size": 100,}])
606
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
607

    
608
  def testDeleteInstance(self):
609
    self.rapi.AddResponse("1234")
610
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
611
    self.assertHandler(rlib2.R_2_instances_name)
612
    self.assertItems(["instance"])
613
    self.assertDryRun()
614

    
615
  def testGetInstanceTags(self):
616
    self.rapi.AddResponse("[]")
617
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
618
    self.assertHandler(rlib2.R_2_instances_name_tags)
619
    self.assertItems(["fooinstance"])
620

    
621
  def testAddInstanceTags(self):
622
    self.rapi.AddResponse("1234")
623
    self.assertEqual(1234,
624
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
625
    self.assertHandler(rlib2.R_2_instances_name_tags)
626
    self.assertItems(["fooinstance"])
627
    self.assertDryRun()
628
    self.assertQuery("tag", ["awesome"])
629

    
630
  def testDeleteInstanceTags(self):
631
    self.rapi.AddResponse("25826")
632
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
633
                                                           dry_run=True))
634
    self.assertHandler(rlib2.R_2_instances_name_tags)
635
    self.assertItems(["foo"])
636
    self.assertDryRun()
637
    self.assertQuery("tag", ["awesome"])
638

    
639
  def testRebootInstance(self):
640
    self.rapi.AddResponse("6146")
641
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
642
                                        ignore_secondaries=True, dry_run=True)
643
    self.assertEqual(6146, job_id)
644
    self.assertHandler(rlib2.R_2_instances_name_reboot)
645
    self.assertItems(["i-bar"])
646
    self.assertDryRun()
647
    self.assertQuery("type", ["hard"])
648
    self.assertQuery("ignore_secondaries", ["1"])
649

    
650
  def testShutdownInstance(self):
651
    self.rapi.AddResponse("1487")
652
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
653
                                                        dry_run=True))
654
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
655
    self.assertItems(["foo-instance"])
656
    self.assertDryRun()
657

    
658
  def testStartupInstance(self):
659
    self.rapi.AddResponse("27149")
660
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
661
                                                        dry_run=True))
662
    self.assertHandler(rlib2.R_2_instances_name_startup)
663
    self.assertItems(["bar-instance"])
664
    self.assertDryRun()
665

    
666
  def testReinstallInstance(self):
667
    self.rapi.AddResponse(serializer.DumpJson([]))
668
    self.rapi.AddResponse("19119")
669
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
670
                                                          os="DOS",
671
                                                          no_startup=True))
672
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
673
    self.assertItems(["baz-instance"])
674
    self.assertQuery("os", ["DOS"])
675
    self.assertQuery("nostartup", ["1"])
676
    self.assertEqual(self.rapi.CountPending(), 0)
677

    
678
  def testReinstallInstanceNew(self):
679
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
680
    self.rapi.AddResponse("25689")
681
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
682
                                                          os="Debian",
683
                                                          no_startup=True))
684
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
685
    self.assertItems(["moo-instance"])
686
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
687
    self.assertEqual(len(data), 2)
688
    self.assertEqual(data["os"], "Debian")
689
    self.assertEqual(data["start"], False)
690
    self.assertEqual(self.rapi.CountPending(), 0)
691

    
692
  def testReinstallInstanceWithOsparams1(self):
693
    self.rapi.AddResponse(serializer.DumpJson([]))
694
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
695
                      "doo-instance", osparams={"x": "y"})
696
    self.assertEqual(self.rapi.CountPending(), 0)
697

    
698
  def testReinstallInstanceWithOsparams2(self):
699
    osparams = {
700
      "Hello": "World",
701
      "foo": "bar",
702
      }
703
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
704
    self.rapi.AddResponse("1717")
705
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
706
                                                         osparams=osparams))
707
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
708
    self.assertItems(["zoo-instance"])
709
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
710
    self.assertEqual(len(data), 2)
711
    self.assertEqual(data["osparams"], osparams)
712
    self.assertEqual(data["start"], True)
713
    self.assertEqual(self.rapi.CountPending(), 0)
714

    
715
  def testReplaceInstanceDisks(self):
716
    self.rapi.AddResponse("999")
717
    job_id = self.client.ReplaceInstanceDisks("instance-name",
718
        disks=[0, 1], dry_run=True, iallocator="hail")
719
    self.assertEqual(999, job_id)
720
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
721
    self.assertItems(["instance-name"])
722
    self.assertQuery("disks", ["0,1"])
723
    self.assertQuery("mode", ["replace_auto"])
724
    self.assertQuery("iallocator", ["hail"])
725
    self.assertDryRun()
726

    
727
    self.rapi.AddResponse("1000")
728
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
729
        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
730
        dry_run=True)
731
    self.assertEqual(1000, job_id)
732
    self.assertItems(["instance-bar"])
733
    self.assertQuery("disks", ["1"])
734
    self.assertQuery("remote_node", ["foo-node"])
735
    self.assertDryRun()
736

    
737
    self.rapi.AddResponse("5175")
738
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
739
    self.assertItems(["instance-moo"])
740
    self.assertQuery("disks", None)
741

    
742
  def testPrepareExport(self):
743
    self.rapi.AddResponse("8326")
744
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
745
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
746
    self.assertItems(["inst1"])
747
    self.assertQuery("mode", ["local"])
748

    
749
  def testExportInstance(self):
750
    self.rapi.AddResponse("19695")
751
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
752
                                        shutdown=True)
753
    self.assertEqual(job_id, 19695)
754
    self.assertHandler(rlib2.R_2_instances_name_export)
755
    self.assertItems(["inst2"])
756

    
757
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
758
    self.assertEqual(data["mode"], "local")
759
    self.assertEqual(data["destination"], "nodeX")
760
    self.assertEqual(data["shutdown"], True)
761

    
762
  def testMigrateInstanceDefaults(self):
763
    self.rapi.AddResponse("24873")
764
    job_id = self.client.MigrateInstance("inst91")
765
    self.assertEqual(job_id, 24873)
766
    self.assertHandler(rlib2.R_2_instances_name_migrate)
767
    self.assertItems(["inst91"])
768

    
769
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
770
    self.assertFalse(data)
771

    
772
  def testMigrateInstance(self):
773
    for mode in constants.HT_MIGRATION_MODES:
774
      for cleanup in [False, True]:
775
        self.rapi.AddResponse("31910")
776
        job_id = self.client.MigrateInstance("inst289", mode=mode,
777
                                             cleanup=cleanup)
778
        self.assertEqual(job_id, 31910)
779
        self.assertHandler(rlib2.R_2_instances_name_migrate)
780
        self.assertItems(["inst289"])
781

    
782
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
783
        self.assertEqual(len(data), 2)
784
        self.assertEqual(data["mode"], mode)
785
        self.assertEqual(data["cleanup"], cleanup)
786

    
787
  def testRenameInstanceDefaults(self):
788
    new_name = "newnametha7euqu"
789
    self.rapi.AddResponse("8791")
790
    job_id = self.client.RenameInstance("inst18821", new_name)
791
    self.assertEqual(job_id, 8791)
792
    self.assertHandler(rlib2.R_2_instances_name_rename)
793
    self.assertItems(["inst18821"])
794

    
795
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
796
    self.assertEqualValues(data, {"new_name": new_name, })
797

    
798
  def testRenameInstance(self):
799
    new_name = "new-name-yiux1iin"
800
    for ip_check in [False, True]:
801
      for name_check in [False, True]:
802
        self.rapi.AddResponse("24776")
803
        job_id = self.client.RenameInstance("inst20967", new_name,
804
                                             ip_check=ip_check,
805
                                             name_check=name_check)
806
        self.assertEqual(job_id, 24776)
807
        self.assertHandler(rlib2.R_2_instances_name_rename)
808
        self.assertItems(["inst20967"])
809

    
810
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
811
        self.assertEqual(len(data), 3)
812
        self.assertEqual(data["new_name"], new_name)
813
        self.assertEqual(data["ip_check"], ip_check)
814
        self.assertEqual(data["name_check"], name_check)
815

    
816
  def testGetJobs(self):
817
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
818
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
819
    self.assertEqual([123, 124], self.client.GetJobs())
820
    self.assertHandler(rlib2.R_2_jobs)
821

    
822
  def testGetJobStatus(self):
823
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
824
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
825
    self.assertHandler(rlib2.R_2_jobs_id)
826
    self.assertItems(["1234"])
827

    
828
  def testWaitForJobChange(self):
829
    fields = ["id", "summary"]
830
    expected = {
831
      "job_info": [123, "something"],
832
      "log_entries": [],
833
      }
834

    
835
    self.rapi.AddResponse(serializer.DumpJson(expected))
836
    result = self.client.WaitForJobChange(123, fields, [], -1)
837
    self.assertEqualValues(expected, result)
838
    self.assertHandler(rlib2.R_2_jobs_id_wait)
839
    self.assertItems(["123"])
840

    
841
  def testCancelJob(self):
842
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
843
    self.assertEqual([True, "Job 123 will be canceled"],
844
                     self.client.CancelJob(999, dry_run=True))
845
    self.assertHandler(rlib2.R_2_jobs_id)
846
    self.assertItems(["999"])
847
    self.assertDryRun()
848

    
849
  def testGetNodes(self):
850
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
851
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
852
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
853
    self.assertHandler(rlib2.R_2_nodes)
854

    
855
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
856
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
857
    self.assertEqual([{"id": "node1", "uri": "uri1"},
858
                      {"id": "node2", "uri": "uri2"}],
859
                     self.client.GetNodes(bulk=True))
860
    self.assertHandler(rlib2.R_2_nodes)
861
    self.assertBulk()
862

    
863
  def testGetNode(self):
864
    self.rapi.AddResponse("{}")
865
    self.assertEqual({}, self.client.GetNode("node-foo"))
866
    self.assertHandler(rlib2.R_2_nodes_name)
867
    self.assertItems(["node-foo"])
868

    
869
  def testEvacuateNode(self):
870
    self.rapi.AddResponse("9876")
871
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
872
    self.assertEqual(9876, job_id)
873
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
874
    self.assertItems(["node-1"])
875
    self.assertQuery("remote_node", ["node-2"])
876

    
877
    self.rapi.AddResponse("8888")
878
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
879
    self.assertEqual(8888, job_id)
880
    self.assertItems(["node-3"])
881
    self.assertQuery("iallocator", ["hail"])
882
    self.assertDryRun()
883

    
884
    self.assertRaises(client.GanetiApiError,
885
                      self.client.EvacuateNode,
886
                      "node-4", iallocator="hail", remote_node="node-5")
887

    
888
  def testMigrateNode(self):
889
    self.rapi.AddResponse("1111")
890
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
891
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
892
    self.assertItems(["node-a"])
893
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
894
    self.assertDryRun()
895

    
896
    self.rapi.AddResponse("1112")
897
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
898
                                                   mode="live"))
899
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
900
    self.assertItems(["node-a"])
901
    self.assertQuery("mode", ["live"])
902
    self.assertDryRun()
903

    
904
  def testGetNodeRole(self):
905
    self.rapi.AddResponse("\"master\"")
906
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
907
    self.assertHandler(rlib2.R_2_nodes_name_role)
908
    self.assertItems(["node-a"])
909

    
910
  def testSetNodeRole(self):
911
    self.rapi.AddResponse("789")
912
    self.assertEqual(789,
913
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
914
    self.assertHandler(rlib2.R_2_nodes_name_role)
915
    self.assertItems(["node-foo"])
916
    self.assertQuery("force", ["1"])
917
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
918

    
919
  def testGetNodeStorageUnits(self):
920
    self.rapi.AddResponse("42")
921
    self.assertEqual(42,
922
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
923
    self.assertHandler(rlib2.R_2_nodes_name_storage)
924
    self.assertItems(["node-x"])
925
    self.assertQuery("storage_type", ["lvm-pv"])
926
    self.assertQuery("output_fields", ["fields"])
927

    
928
  def testModifyNodeStorageUnits(self):
929
    self.rapi.AddResponse("14")
930
    self.assertEqual(14,
931
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
932
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
933
    self.assertItems(["node-z"])
934
    self.assertQuery("storage_type", ["lvm-pv"])
935
    self.assertQuery("name", ["hda"])
936
    self.assertQuery("allocatable", None)
937

    
938
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
939
      self.rapi.AddResponse("7205")
940
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
941
                                                  allocatable=allocatable)
942
      self.assertEqual(7205, job_id)
943
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
944
      self.assertItems(["node-z"])
945
      self.assertQuery("storage_type", ["lvm-pv"])
946
      self.assertQuery("name", ["hda"])
947
      self.assertQuery("allocatable", [query_allocatable])
948

    
949
  def testRepairNodeStorageUnits(self):
950
    self.rapi.AddResponse("99")
951
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
952
                                                            "hda"))
953
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
954
    self.assertItems(["node-z"])
955
    self.assertQuery("storage_type", ["lvm-pv"])
956
    self.assertQuery("name", ["hda"])
957

    
958
  def testGetNodeTags(self):
959
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
960
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
961
    self.assertHandler(rlib2.R_2_nodes_name_tags)
962
    self.assertItems(["node-k"])
963

    
964
  def testAddNodeTags(self):
965
    self.rapi.AddResponse("1234")
966
    self.assertEqual(1234,
967
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
968
    self.assertHandler(rlib2.R_2_nodes_name_tags)
969
    self.assertItems(["node-v"])
970
    self.assertDryRun()
971
    self.assertQuery("tag", ["awesome"])
972

    
973
  def testDeleteNodeTags(self):
974
    self.rapi.AddResponse("16861")
975
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
976
                                                       dry_run=True))
977
    self.assertHandler(rlib2.R_2_nodes_name_tags)
978
    self.assertItems(["node-w"])
979
    self.assertDryRun()
980
    self.assertQuery("tag", ["awesome"])
981

    
982
  def testGetGroups(self):
983
    groups = [{"name": "group1",
984
               "uri": "/2/groups/group1",
985
               },
986
              {"name": "group2",
987
               "uri": "/2/groups/group2",
988
               },
989
              ]
990
    self.rapi.AddResponse(serializer.DumpJson(groups))
991
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
992
    self.assertHandler(rlib2.R_2_groups)
993

    
994
  def testGetGroupsBulk(self):
995
    groups = [{"name": "group1",
996
               "uri": "/2/groups/group1",
997
               "node_cnt": 2,
998
               "node_list": ["gnt1.test",
999
                             "gnt2.test",
1000
                             ],
1001
               },
1002
              {"name": "group2",
1003
               "uri": "/2/groups/group2",
1004
               "node_cnt": 1,
1005
               "node_list": ["gnt3.test",
1006
                             ],
1007
               },
1008
              ]
1009
    self.rapi.AddResponse(serializer.DumpJson(groups))
1010

    
1011
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
1012
    self.assertHandler(rlib2.R_2_groups)
1013
    self.assertBulk()
1014

    
1015
  def testGetGroup(self):
1016
    group = {"ctime": None,
1017
             "name": "default",
1018
             }
1019
    self.rapi.AddResponse(serializer.DumpJson(group))
1020
    self.assertEqual({"ctime": None, "name": "default"},
1021
                     self.client.GetGroup("default"))
1022
    self.assertHandler(rlib2.R_2_groups_name)
1023
    self.assertItems(["default"])
1024

    
1025
  def testCreateGroup(self):
1026
    self.rapi.AddResponse("12345")
1027
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
1028
    self.assertEqual(job_id, 12345)
1029
    self.assertHandler(rlib2.R_2_groups)
1030
    self.assertDryRun()
1031

    
1032
  def testDeleteGroup(self):
1033
    self.rapi.AddResponse("12346")
1034
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1035
    self.assertEqual(job_id, 12346)
1036
    self.assertHandler(rlib2.R_2_groups_name)
1037
    self.assertDryRun()
1038

    
1039
  def testRenameGroup(self):
1040
    self.rapi.AddResponse("12347")
1041
    job_id = self.client.RenameGroup("oldname", "newname")
1042
    self.assertEqual(job_id, 12347)
1043
    self.assertHandler(rlib2.R_2_groups_name_rename)
1044

    
1045
  def testModifyGroup(self):
1046
    self.rapi.AddResponse("12348")
1047
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1048
    self.assertEqual(job_id, 12348)
1049
    self.assertHandler(rlib2.R_2_groups_name_modify)
1050

    
1051
  def testAssignGroupNodes(self):
1052
    self.rapi.AddResponse("12349")
1053
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1054
                                          force=True, dry_run=True)
1055
    self.assertEqual(job_id, 12349)
1056
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1057
    self.assertDryRun()
1058
    self.assertUseForce()
1059

    
1060
  def testModifyCluster(self):
1061
    for mnh in [None, False, True]:
1062
      self.rapi.AddResponse("14470")
1063
      self.assertEqual(14470,
1064
        self.client.ModifyCluster(maintain_node_health=mnh))
1065
      self.assertHandler(rlib2.R_2_cluster_modify)
1066
      self.assertItems([])
1067
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1068
      self.assertEqual(len(data), 1)
1069
      self.assertEqual(data["maintain_node_health"], mnh)
1070
      self.assertEqual(self.rapi.CountPending(), 0)
1071

    
1072
  def testGrowInstanceDisk(self):
1073
    for idx, wait_for_sync in enumerate([None, False, True]):
1074
      amount = 128 + (512 * idx)
1075
      self.assertEqual(self.rapi.CountPending(), 0)
1076
      self.rapi.AddResponse("30783")
1077
      self.assertEqual(30783,
1078
        self.client.GrowInstanceDisk("eze8ch", idx, amount,
1079
                                     wait_for_sync=wait_for_sync))
1080
      self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1081
      self.assertItems(["eze8ch", str(idx)])
1082
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1083
      if wait_for_sync is None:
1084
        self.assertEqual(len(data), 1)
1085
        self.assert_("wait_for_sync" not in data)
1086
      else:
1087
        self.assertEqual(len(data), 2)
1088
        self.assertEqual(data["wait_for_sync"], wait_for_sync)
1089
      self.assertEqual(data["amount"], amount)
1090
      self.assertEqual(self.rapi.CountPending(), 0)
1091

    
1092

    
1093
if __name__ == '__main__':
1094
  client.UsesRapiClient(testutils.GanetiTestProgram)()