Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 9a8ae794

History | View | Annotate | Download (44.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
from ganeti import utils
34
from ganeti import query
35
from ganeti import objects
36

    
37
from ganeti.rapi import connector
38
from ganeti.rapi import rlib2
39
from ganeti.rapi import client
40

    
41
import testutils
42

    
43

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

    
46
# List of resource handlers which aren't used by the RAPI client
47
_KNOWN_UNUSED = set([
48
  connector.R_root,
49
  connector.R_2,
50
  ])
51

    
52
# Global variable for collecting used handlers
53
_used_handlers = None
54

    
55

    
56
def _GetPathFromUri(uri):
57
  """Gets the path and query from a URI.
58

59
  """
60
  match = _URI_RE.match(uri)
61
  if match:
62
    return match.groupdict()["path"]
63
  else:
64
    return None
65

    
66

    
67
class FakeCurl:
68
  def __init__(self, rapi):
69
    self._rapi = rapi
70
    self._opts = {}
71
    self._info = {}
72

    
73
  def setopt(self, opt, value):
74
    self._opts[opt] = value
75

    
76
  def getopt(self, opt):
77
    return self._opts.get(opt)
78

    
79
  def unsetopt(self, opt):
80
    self._opts.pop(opt, None)
81

    
82
  def getinfo(self, info):
83
    return self._info[info]
84

    
85
  def perform(self):
86
    method = self._opts[pycurl.CUSTOMREQUEST]
87
    url = self._opts[pycurl.URL]
88
    request_body = self._opts[pycurl.POSTFIELDS]
89
    writefn = self._opts[pycurl.WRITEFUNCTION]
90

    
91
    path = _GetPathFromUri(url)
92
    (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
93

    
94
    self._info[pycurl.RESPONSE_CODE] = code
95
    if resp_body is not None:
96
      writefn(resp_body)
97

    
98

    
99
class RapiMock(object):
100
  def __init__(self):
101
    self._mapper = connector.Mapper()
102
    self._responses = []
103
    self._last_handler = None
104
    self._last_req_data = None
105

    
106
  def ResetResponses(self):
107
    del self._responses[:]
108

    
109
  def AddResponse(self, response, code=200):
110
    self._responses.insert(0, (code, response))
111

    
112
  def CountPending(self):
113
    return len(self._responses)
114

    
115
  def GetLastHandler(self):
116
    return self._last_handler
117

    
118
  def GetLastRequestData(self):
119
    return self._last_req_data
120

    
121
  def FetchResponse(self, path, method, request_body):
122
    self._last_req_data = request_body
123

    
124
    try:
125
      (handler_cls, items, args) = self._mapper.getController(path)
126

    
127
      # Record handler as used
128
      _used_handlers.add(handler_cls)
129

    
130
      self._last_handler = handler_cls(items, args, None)
131
      if not hasattr(self._last_handler, method.upper()):
132
        raise http.HttpNotImplemented(message="Method not implemented")
133

    
134
    except http.HttpException, ex:
135
      code = ex.code
136
      response = ex.message
137
    else:
138
      if not self._responses:
139
        raise Exception("No responses")
140

    
141
      (code, response) = self._responses.pop()
142

    
143
    return code, response
144

    
145

    
146
class TestConstants(unittest.TestCase):
147
  def test(self):
148
    self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
149
    self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
150
    self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
151
    self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
152
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
153
    self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
154
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
155
    self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
156
    self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITLOCK)
157
    self.assertEqual(client.JOB_STATUS_CANCELING,
158
                     constants.JOB_STATUS_CANCELING)
159
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
160
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
161
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
162
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
163
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
164
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
165

    
166

    
167
class RapiMockTest(unittest.TestCase):
168
  def test(self):
169
    rapi = RapiMock()
170
    path = "/version"
171
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
172
    self.assertEqual((501, "Method not implemented"),
173
                     rapi.FetchResponse("/version", "POST", None))
174
    rapi.AddResponse("2")
175
    code, response = rapi.FetchResponse("/version", "GET", None)
176
    self.assertEqual(200, code)
177
    self.assertEqual("2", response)
178
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
179

    
180

    
181
def _FakeNoSslPycurlVersion():
182
  # Note: incomplete version tuple
183
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
184

    
185

    
186
def _FakeFancySslPycurlVersion():
187
  # Note: incomplete version tuple
188
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
189

    
190

    
191
def _FakeOpenSslPycurlVersion():
192
  # Note: incomplete version tuple
193
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
194

    
195

    
196
def _FakeGnuTlsPycurlVersion():
197
  # Note: incomplete version tuple
198
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
199

    
200

    
201
class TestExtendedConfig(unittest.TestCase):
202
  def testAuth(self):
203
    cl = client.GanetiRapiClient("master.example.com",
204
                                 username="user", password="pw",
205
                                 curl_factory=lambda: FakeCurl(RapiMock()))
206

    
207
    curl = cl._CreateCurl()
208
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
209
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
210

    
211
  def testInvalidAuth(self):
212
    # No username
213
    self.assertRaises(client.Error, client.GanetiRapiClient,
214
                      "master-a.example.com", password="pw")
215
    # No password
216
    self.assertRaises(client.Error, client.GanetiRapiClient,
217
                      "master-b.example.com", username="user")
218

    
219
  def testCertVerifyInvalidCombinations(self):
220
    self.assertRaises(client.Error, client.GenericCurlConfig,
221
                      use_curl_cabundle=True, cafile="cert1.pem")
222
    self.assertRaises(client.Error, client.GenericCurlConfig,
223
                      use_curl_cabundle=True, capath="certs/")
224
    self.assertRaises(client.Error, client.GenericCurlConfig,
225
                      use_curl_cabundle=True,
226
                      cafile="cert1.pem", capath="certs/")
227

    
228
  def testProxySignalVerifyHostname(self):
229
    for use_gnutls in [False, True]:
230
      if use_gnutls:
231
        pcverfn = _FakeGnuTlsPycurlVersion
232
      else:
233
        pcverfn = _FakeOpenSslPycurlVersion
234

    
235
      for proxy in ["", "http://127.0.0.1:1234"]:
236
        for use_signal in [False, True]:
237
          for verify_hostname in [False, True]:
238
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
239
                                             verify_hostname=verify_hostname,
240
                                             _pycurl_version_fn=pcverfn)
241

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

    
247
            curl = cl._CreateCurl()
248
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
249
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
250

    
251
            if verify_hostname:
252
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
253
            else:
254
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
255

    
256
  def testNoCertVerify(self):
257
    cfgfn = client.GenericCurlConfig()
258

    
259
    curl_factory = lambda: FakeCurl(RapiMock())
260
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
261
                                 curl_factory=curl_factory)
262

    
263
    curl = cl._CreateCurl()
264
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
265
    self.assertFalse(curl.getopt(pycurl.CAINFO))
266
    self.assertFalse(curl.getopt(pycurl.CAPATH))
267

    
268
  def testCertVerifyCurlBundle(self):
269
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
270

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

    
275
    curl = cl._CreateCurl()
276
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
277
    self.assertFalse(curl.getopt(pycurl.CAINFO))
278
    self.assertFalse(curl.getopt(pycurl.CAPATH))
279

    
280
  def testCertVerifyCafile(self):
281
    mycert = "/tmp/some/UNUSED/cert/file.pem"
282
    cfgfn = client.GenericCurlConfig(cafile=mycert)
283

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

    
288
    curl = cl._CreateCurl()
289
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
290
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
291
    self.assertFalse(curl.getopt(pycurl.CAPATH))
292

    
293
  def testCertVerifyCapath(self):
294
    certdir = "/tmp/some/UNUSED/cert/directory"
295
    pcverfn = _FakeOpenSslPycurlVersion
296
    cfgfn = client.GenericCurlConfig(capath=certdir,
297
                                     _pycurl_version_fn=pcverfn)
298

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

    
303
    curl = cl._CreateCurl()
304
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
305
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
306
    self.assertFalse(curl.getopt(pycurl.CAINFO))
307

    
308
  def testCertVerifyCapathGnuTls(self):
309
    certdir = "/tmp/some/UNUSED/cert/directory"
310
    pcverfn = _FakeGnuTlsPycurlVersion
311
    cfgfn = client.GenericCurlConfig(capath=certdir,
312
                                     _pycurl_version_fn=pcverfn)
313

    
314
    curl_factory = lambda: FakeCurl(RapiMock())
315
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
316
                                 curl_factory=curl_factory)
317

    
318
    self.assertRaises(client.Error, cl._CreateCurl)
319

    
320
  def testCertVerifyNoSsl(self):
321
    certdir = "/tmp/some/UNUSED/cert/directory"
322
    pcverfn = _FakeNoSslPycurlVersion
323
    cfgfn = client.GenericCurlConfig(capath=certdir,
324
                                     _pycurl_version_fn=pcverfn)
325

    
326
    curl_factory = lambda: FakeCurl(RapiMock())
327
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
328
                                 curl_factory=curl_factory)
329

    
330
    self.assertRaises(client.Error, cl._CreateCurl)
331

    
332
  def testCertVerifyFancySsl(self):
333
    certdir = "/tmp/some/UNUSED/cert/directory"
334
    pcverfn = _FakeFancySslPycurlVersion
335
    cfgfn = client.GenericCurlConfig(capath=certdir,
336
                                     _pycurl_version_fn=pcverfn)
337

    
338
    curl_factory = lambda: FakeCurl(RapiMock())
339
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
340
                                 curl_factory=curl_factory)
341

    
342
    self.assertRaises(NotImplementedError, cl._CreateCurl)
343

    
344
  def testCertVerifyCapath(self):
345
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
346
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
347
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
348
                                         timeout=timeout)
349

    
350
        curl_factory = lambda: FakeCurl(RapiMock())
351
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
352
                                     curl_factory=curl_factory)
353

    
354
        curl = cl._CreateCurl()
355
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
356
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
357

    
358

    
359
class GanetiRapiClientTests(testutils.GanetiTestCase):
360
  def setUp(self):
361
    testutils.GanetiTestCase.setUp(self)
362

    
363
    self.rapi = RapiMock()
364
    self.curl = FakeCurl(self.rapi)
365
    self.client = client.GanetiRapiClient("master.example.com",
366
                                          curl_factory=lambda: self.curl)
367

    
368
  def assertHandler(self, handler_cls):
369
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
370

    
371
  def assertQuery(self, key, value):
372
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
373

    
374
  def assertItems(self, items):
375
    self.assertEqual(items, self.rapi.GetLastHandler().items)
376

    
377
  def assertBulk(self):
378
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
379

    
380
  def assertDryRun(self):
381
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
382

    
383
  def assertUseForce(self):
384
    self.assertTrue(self.rapi.GetLastHandler().useForce())
385

    
386
  def testEncodeQuery(self):
387
    query = [
388
      ("a", None),
389
      ("b", 1),
390
      ("c", 2),
391
      ("d", "Foo"),
392
      ("e", True),
393
      ]
394

    
395
    expected = [
396
      ("a", ""),
397
      ("b", 1),
398
      ("c", 2),
399
      ("d", "Foo"),
400
      ("e", 1),
401
      ]
402

    
403
    self.assertEqualValues(self.client._EncodeQuery(query),
404
                           expected)
405

    
406
    # invalid types
407
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
408
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
409

    
410
  def testCurlSettings(self):
411
    self.rapi.AddResponse("2")
412
    self.assertEqual(2, self.client.GetVersion())
413
    self.assertHandler(rlib2.R_version)
414

    
415
    # Signals should be disabled by default
416
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
417

    
418
    # No auth and no proxy
419
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
420
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
421

    
422
    # Content-type is required for requests
423
    headers = self.curl.getopt(pycurl.HTTPHEADER)
424
    self.assert_("Content-type: application/json" in headers)
425

    
426
  def testHttpError(self):
427
    self.rapi.AddResponse(None, code=404)
428
    try:
429
      self.client.GetJobStatus(15140)
430
    except client.GanetiApiError, err:
431
      self.assertEqual(err.code, 404)
432
    else:
433
      self.fail("Didn't raise exception")
434

    
435
  def testGetVersion(self):
436
    self.rapi.AddResponse("2")
437
    self.assertEqual(2, self.client.GetVersion())
438
    self.assertHandler(rlib2.R_version)
439

    
440
  def testGetFeatures(self):
441
    for features in [[], ["foo", "bar", "baz"]]:
442
      self.rapi.AddResponse(serializer.DumpJson(features))
443
      self.assertEqual(features, self.client.GetFeatures())
444
      self.assertHandler(rlib2.R_2_features)
445

    
446
  def testGetFeaturesNotFound(self):
447
    self.rapi.AddResponse(None, code=404)
448
    self.assertEqual([], self.client.GetFeatures())
449

    
450
  def testGetOperatingSystems(self):
451
    self.rapi.AddResponse("[\"beos\"]")
452
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
453
    self.assertHandler(rlib2.R_2_os)
454

    
455
  def testGetClusterTags(self):
456
    self.rapi.AddResponse("[\"tag\"]")
457
    self.assertEqual(["tag"], self.client.GetClusterTags())
458
    self.assertHandler(rlib2.R_2_tags)
459

    
460
  def testAddClusterTags(self):
461
    self.rapi.AddResponse("1234")
462
    self.assertEqual(1234,
463
        self.client.AddClusterTags(["awesome"], dry_run=True))
464
    self.assertHandler(rlib2.R_2_tags)
465
    self.assertDryRun()
466
    self.assertQuery("tag", ["awesome"])
467

    
468
  def testDeleteClusterTags(self):
469
    self.rapi.AddResponse("5107")
470
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
471
                                                         dry_run=True))
472
    self.assertHandler(rlib2.R_2_tags)
473
    self.assertDryRun()
474
    self.assertQuery("tag", ["awesome"])
475

    
476
  def testGetInfo(self):
477
    self.rapi.AddResponse("{}")
478
    self.assertEqual({}, self.client.GetInfo())
479
    self.assertHandler(rlib2.R_2_info)
480

    
481
  def testGetInstances(self):
482
    self.rapi.AddResponse("[]")
483
    self.assertEqual([], self.client.GetInstances(bulk=True))
484
    self.assertHandler(rlib2.R_2_instances)
485
    self.assertBulk()
486

    
487
  def testGetInstance(self):
488
    self.rapi.AddResponse("[]")
489
    self.assertEqual([], self.client.GetInstance("instance"))
490
    self.assertHandler(rlib2.R_2_instances_name)
491
    self.assertItems(["instance"])
492

    
493
  def testGetInstanceInfo(self):
494
    self.rapi.AddResponse("21291")
495
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
496
    self.assertHandler(rlib2.R_2_instances_name_info)
497
    self.assertItems(["inst3"])
498
    self.assertQuery("static", None)
499

    
500
    self.rapi.AddResponse("3428")
501
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
502
    self.assertHandler(rlib2.R_2_instances_name_info)
503
    self.assertItems(["inst31"])
504
    self.assertQuery("static", ["0"])
505

    
506
    self.rapi.AddResponse("15665")
507
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
508
    self.assertHandler(rlib2.R_2_instances_name_info)
509
    self.assertItems(["inst32"])
510
    self.assertQuery("static", ["1"])
511

    
512
  def testCreateInstanceOldVersion(self):
513
    # The old request format, version 0, is no longer supported
514
    self.rapi.AddResponse(None, code=404)
515
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
516
                      "create", "inst1.example.com", "plain", [], [])
517
    self.assertEqual(self.rapi.CountPending(), 0)
518

    
519
  def testCreateInstance(self):
520
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
521
    self.rapi.AddResponse("23030")
522
    job_id = self.client.CreateInstance("create", "inst1.example.com",
523
                                        "plain", [], [], dry_run=True)
524
    self.assertEqual(job_id, 23030)
525
    self.assertHandler(rlib2.R_2_instances)
526
    self.assertDryRun()
527

    
528
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
529

    
530
    for field in ["dry_run", "beparams", "hvparams", "start"]:
531
      self.assertFalse(field in data)
532

    
533
    self.assertEqual(data["name"], "inst1.example.com")
534
    self.assertEqual(data["disk_template"], "plain")
535

    
536
  def testCreateInstance2(self):
537
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
538
    self.rapi.AddResponse("24740")
539
    job_id = self.client.CreateInstance("import", "inst2.example.com",
540
                                        "drbd8", [{"size": 100,}],
541
                                        [{}, {"bridge": "br1", }],
542
                                        dry_run=False, start=True,
543
                                        pnode="node1", snode="node9",
544
                                        ip_check=False)
545
    self.assertEqual(job_id, 24740)
546
    self.assertHandler(rlib2.R_2_instances)
547

    
548
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
549
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
550
    self.assertEqual(data["name"], "inst2.example.com")
551
    self.assertEqual(data["disk_template"], "drbd8")
552
    self.assertEqual(data["start"], True)
553
    self.assertEqual(data["ip_check"], False)
554
    self.assertEqualValues(data["disks"], [{"size": 100,}])
555
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
556

    
557
  def testDeleteInstance(self):
558
    self.rapi.AddResponse("1234")
559
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
560
    self.assertHandler(rlib2.R_2_instances_name)
561
    self.assertItems(["instance"])
562
    self.assertDryRun()
563

    
564
  def testGetInstanceTags(self):
565
    self.rapi.AddResponse("[]")
566
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
567
    self.assertHandler(rlib2.R_2_instances_name_tags)
568
    self.assertItems(["fooinstance"])
569

    
570
  def testAddInstanceTags(self):
571
    self.rapi.AddResponse("1234")
572
    self.assertEqual(1234,
573
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
574
    self.assertHandler(rlib2.R_2_instances_name_tags)
575
    self.assertItems(["fooinstance"])
576
    self.assertDryRun()
577
    self.assertQuery("tag", ["awesome"])
578

    
579
  def testDeleteInstanceTags(self):
580
    self.rapi.AddResponse("25826")
581
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
582
                                                           dry_run=True))
583
    self.assertHandler(rlib2.R_2_instances_name_tags)
584
    self.assertItems(["foo"])
585
    self.assertDryRun()
586
    self.assertQuery("tag", ["awesome"])
587

    
588
  def testRebootInstance(self):
589
    self.rapi.AddResponse("6146")
590
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
591
                                        ignore_secondaries=True, dry_run=True)
592
    self.assertEqual(6146, job_id)
593
    self.assertHandler(rlib2.R_2_instances_name_reboot)
594
    self.assertItems(["i-bar"])
595
    self.assertDryRun()
596
    self.assertQuery("type", ["hard"])
597
    self.assertQuery("ignore_secondaries", ["1"])
598

    
599
  def testShutdownInstance(self):
600
    self.rapi.AddResponse("1487")
601
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
602
                                                        dry_run=True))
603
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
604
    self.assertItems(["foo-instance"])
605
    self.assertDryRun()
606

    
607
  def testStartupInstance(self):
608
    self.rapi.AddResponse("27149")
609
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
610
                                                        dry_run=True))
611
    self.assertHandler(rlib2.R_2_instances_name_startup)
612
    self.assertItems(["bar-instance"])
613
    self.assertDryRun()
614

    
615
  def testReinstallInstance(self):
616
    self.rapi.AddResponse(serializer.DumpJson([]))
617
    self.rapi.AddResponse("19119")
618
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
619
                                                          os="DOS",
620
                                                          no_startup=True))
621
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
622
    self.assertItems(["baz-instance"])
623
    self.assertQuery("os", ["DOS"])
624
    self.assertQuery("nostartup", ["1"])
625
    self.assertEqual(self.rapi.CountPending(), 0)
626

    
627
  def testReinstallInstanceNew(self):
628
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
629
    self.rapi.AddResponse("25689")
630
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
631
                                                          os="Debian",
632
                                                          no_startup=True))
633
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
634
    self.assertItems(["moo-instance"])
635
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
636
    self.assertEqual(len(data), 2)
637
    self.assertEqual(data["os"], "Debian")
638
    self.assertEqual(data["start"], False)
639
    self.assertEqual(self.rapi.CountPending(), 0)
640

    
641
  def testReinstallInstanceWithOsparams1(self):
642
    self.rapi.AddResponse(serializer.DumpJson([]))
643
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
644
                      "doo-instance", osparams={"x": "y"})
645
    self.assertEqual(self.rapi.CountPending(), 0)
646

    
647
  def testReinstallInstanceWithOsparams2(self):
648
    osparams = {
649
      "Hello": "World",
650
      "foo": "bar",
651
      }
652
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
653
    self.rapi.AddResponse("1717")
654
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
655
                                                         osparams=osparams))
656
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
657
    self.assertItems(["zoo-instance"])
658
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
659
    self.assertEqual(len(data), 2)
660
    self.assertEqual(data["osparams"], osparams)
661
    self.assertEqual(data["start"], True)
662
    self.assertEqual(self.rapi.CountPending(), 0)
663

    
664
  def testReplaceInstanceDisks(self):
665
    self.rapi.AddResponse("999")
666
    job_id = self.client.ReplaceInstanceDisks("instance-name",
667
        disks=[0, 1], dry_run=True, iallocator="hail")
668
    self.assertEqual(999, job_id)
669
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
670
    self.assertItems(["instance-name"])
671
    self.assertQuery("disks", ["0,1"])
672
    self.assertQuery("mode", ["replace_auto"])
673
    self.assertQuery("iallocator", ["hail"])
674
    self.assertDryRun()
675

    
676
    self.rapi.AddResponse("1000")
677
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
678
        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
679
        dry_run=True)
680
    self.assertEqual(1000, job_id)
681
    self.assertItems(["instance-bar"])
682
    self.assertQuery("disks", ["1"])
683
    self.assertQuery("remote_node", ["foo-node"])
684
    self.assertDryRun()
685

    
686
    self.rapi.AddResponse("5175")
687
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
688
    self.assertItems(["instance-moo"])
689
    self.assertQuery("disks", None)
690

    
691
  def testPrepareExport(self):
692
    self.rapi.AddResponse("8326")
693
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
694
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
695
    self.assertItems(["inst1"])
696
    self.assertQuery("mode", ["local"])
697

    
698
  def testExportInstance(self):
699
    self.rapi.AddResponse("19695")
700
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
701
                                        shutdown=True)
702
    self.assertEqual(job_id, 19695)
703
    self.assertHandler(rlib2.R_2_instances_name_export)
704
    self.assertItems(["inst2"])
705

    
706
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
707
    self.assertEqual(data["mode"], "local")
708
    self.assertEqual(data["destination"], "nodeX")
709
    self.assertEqual(data["shutdown"], True)
710

    
711
  def testMigrateInstanceDefaults(self):
712
    self.rapi.AddResponse("24873")
713
    job_id = self.client.MigrateInstance("inst91")
714
    self.assertEqual(job_id, 24873)
715
    self.assertHandler(rlib2.R_2_instances_name_migrate)
716
    self.assertItems(["inst91"])
717

    
718
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
719
    self.assertFalse(data)
720

    
721
  def testMigrateInstance(self):
722
    for mode in constants.HT_MIGRATION_MODES:
723
      for cleanup in [False, True]:
724
        self.rapi.AddResponse("31910")
725
        job_id = self.client.MigrateInstance("inst289", mode=mode,
726
                                             cleanup=cleanup)
727
        self.assertEqual(job_id, 31910)
728
        self.assertHandler(rlib2.R_2_instances_name_migrate)
729
        self.assertItems(["inst289"])
730

    
731
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
732
        self.assertEqual(len(data), 2)
733
        self.assertEqual(data["mode"], mode)
734
        self.assertEqual(data["cleanup"], cleanup)
735

    
736
  def testRenameInstanceDefaults(self):
737
    new_name = "newnametha7euqu"
738
    self.rapi.AddResponse("8791")
739
    job_id = self.client.RenameInstance("inst18821", new_name)
740
    self.assertEqual(job_id, 8791)
741
    self.assertHandler(rlib2.R_2_instances_name_rename)
742
    self.assertItems(["inst18821"])
743

    
744
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
745
    self.assertEqualValues(data, {"new_name": new_name, })
746

    
747
  def testRenameInstance(self):
748
    new_name = "new-name-yiux1iin"
749
    for ip_check in [False, True]:
750
      for name_check in [False, True]:
751
        self.rapi.AddResponse("24776")
752
        job_id = self.client.RenameInstance("inst20967", new_name,
753
                                             ip_check=ip_check,
754
                                             name_check=name_check)
755
        self.assertEqual(job_id, 24776)
756
        self.assertHandler(rlib2.R_2_instances_name_rename)
757
        self.assertItems(["inst20967"])
758

    
759
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
760
        self.assertEqual(len(data), 3)
761
        self.assertEqual(data["new_name"], new_name)
762
        self.assertEqual(data["ip_check"], ip_check)
763
        self.assertEqual(data["name_check"], name_check)
764

    
765
  def testGetJobs(self):
766
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
767
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
768
    self.assertEqual([123, 124], self.client.GetJobs())
769
    self.assertHandler(rlib2.R_2_jobs)
770

    
771
  def testGetJobStatus(self):
772
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
773
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
774
    self.assertHandler(rlib2.R_2_jobs_id)
775
    self.assertItems(["1234"])
776

    
777
  def testWaitForJobChange(self):
778
    fields = ["id", "summary"]
779
    expected = {
780
      "job_info": [123, "something"],
781
      "log_entries": [],
782
      }
783

    
784
    self.rapi.AddResponse(serializer.DumpJson(expected))
785
    result = self.client.WaitForJobChange(123, fields, [], -1)
786
    self.assertEqualValues(expected, result)
787
    self.assertHandler(rlib2.R_2_jobs_id_wait)
788
    self.assertItems(["123"])
789

    
790
  def testCancelJob(self):
791
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
792
    self.assertEqual([True, "Job 123 will be canceled"],
793
                     self.client.CancelJob(999, dry_run=True))
794
    self.assertHandler(rlib2.R_2_jobs_id)
795
    self.assertItems(["999"])
796
    self.assertDryRun()
797

    
798
  def testGetNodes(self):
799
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
800
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
801
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
802
    self.assertHandler(rlib2.R_2_nodes)
803

    
804
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
805
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
806
    self.assertEqual([{"id": "node1", "uri": "uri1"},
807
                      {"id": "node2", "uri": "uri2"}],
808
                     self.client.GetNodes(bulk=True))
809
    self.assertHandler(rlib2.R_2_nodes)
810
    self.assertBulk()
811

    
812
  def testGetNode(self):
813
    self.rapi.AddResponse("{}")
814
    self.assertEqual({}, self.client.GetNode("node-foo"))
815
    self.assertHandler(rlib2.R_2_nodes_name)
816
    self.assertItems(["node-foo"])
817

    
818
  def testEvacuateNode(self):
819
    self.rapi.AddResponse("9876")
820
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
821
    self.assertEqual(9876, job_id)
822
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
823
    self.assertItems(["node-1"])
824
    self.assertQuery("remote_node", ["node-2"])
825

    
826
    self.rapi.AddResponse("8888")
827
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
828
    self.assertEqual(8888, job_id)
829
    self.assertItems(["node-3"])
830
    self.assertQuery("iallocator", ["hail"])
831
    self.assertDryRun()
832

    
833
    self.assertRaises(client.GanetiApiError,
834
                      self.client.EvacuateNode,
835
                      "node-4", iallocator="hail", remote_node="node-5")
836

    
837
  def testMigrateNode(self):
838
    self.rapi.AddResponse("1111")
839
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
840
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
841
    self.assertItems(["node-a"])
842
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
843
    self.assertDryRun()
844

    
845
    self.rapi.AddResponse("1112")
846
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
847
                                                   mode="live"))
848
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
849
    self.assertItems(["node-a"])
850
    self.assertQuery("mode", ["live"])
851
    self.assertDryRun()
852

    
853
  def testGetNodeRole(self):
854
    self.rapi.AddResponse("\"master\"")
855
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
856
    self.assertHandler(rlib2.R_2_nodes_name_role)
857
    self.assertItems(["node-a"])
858

    
859
  def testSetNodeRole(self):
860
    self.rapi.AddResponse("789")
861
    self.assertEqual(789,
862
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
863
    self.assertHandler(rlib2.R_2_nodes_name_role)
864
    self.assertItems(["node-foo"])
865
    self.assertQuery("force", ["1"])
866
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
867

    
868
  def testGetNodeStorageUnits(self):
869
    self.rapi.AddResponse("42")
870
    self.assertEqual(42,
871
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
872
    self.assertHandler(rlib2.R_2_nodes_name_storage)
873
    self.assertItems(["node-x"])
874
    self.assertQuery("storage_type", ["lvm-pv"])
875
    self.assertQuery("output_fields", ["fields"])
876

    
877
  def testModifyNodeStorageUnits(self):
878
    self.rapi.AddResponse("14")
879
    self.assertEqual(14,
880
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
881
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
882
    self.assertItems(["node-z"])
883
    self.assertQuery("storage_type", ["lvm-pv"])
884
    self.assertQuery("name", ["hda"])
885
    self.assertQuery("allocatable", None)
886

    
887
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
888
      self.rapi.AddResponse("7205")
889
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
890
                                                  allocatable=allocatable)
891
      self.assertEqual(7205, job_id)
892
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
893
      self.assertItems(["node-z"])
894
      self.assertQuery("storage_type", ["lvm-pv"])
895
      self.assertQuery("name", ["hda"])
896
      self.assertQuery("allocatable", [query_allocatable])
897

    
898
  def testRepairNodeStorageUnits(self):
899
    self.rapi.AddResponse("99")
900
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
901
                                                            "hda"))
902
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
903
    self.assertItems(["node-z"])
904
    self.assertQuery("storage_type", ["lvm-pv"])
905
    self.assertQuery("name", ["hda"])
906

    
907
  def testGetNodeTags(self):
908
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
909
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
910
    self.assertHandler(rlib2.R_2_nodes_name_tags)
911
    self.assertItems(["node-k"])
912

    
913
  def testAddNodeTags(self):
914
    self.rapi.AddResponse("1234")
915
    self.assertEqual(1234,
916
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
917
    self.assertHandler(rlib2.R_2_nodes_name_tags)
918
    self.assertItems(["node-v"])
919
    self.assertDryRun()
920
    self.assertQuery("tag", ["awesome"])
921

    
922
  def testDeleteNodeTags(self):
923
    self.rapi.AddResponse("16861")
924
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
925
                                                       dry_run=True))
926
    self.assertHandler(rlib2.R_2_nodes_name_tags)
927
    self.assertItems(["node-w"])
928
    self.assertDryRun()
929
    self.assertQuery("tag", ["awesome"])
930

    
931
  def testGetGroups(self):
932
    groups = [{"name": "group1",
933
               "uri": "/2/groups/group1",
934
               },
935
              {"name": "group2",
936
               "uri": "/2/groups/group2",
937
               },
938
              ]
939
    self.rapi.AddResponse(serializer.DumpJson(groups))
940
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
941
    self.assertHandler(rlib2.R_2_groups)
942

    
943
  def testGetGroupsBulk(self):
944
    groups = [{"name": "group1",
945
               "uri": "/2/groups/group1",
946
               "node_cnt": 2,
947
               "node_list": ["gnt1.test",
948
                             "gnt2.test",
949
                             ],
950
               },
951
              {"name": "group2",
952
               "uri": "/2/groups/group2",
953
               "node_cnt": 1,
954
               "node_list": ["gnt3.test",
955
                             ],
956
               },
957
              ]
958
    self.rapi.AddResponse(serializer.DumpJson(groups))
959

    
960
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
961
    self.assertHandler(rlib2.R_2_groups)
962
    self.assertBulk()
963

    
964
  def testGetGroup(self):
965
    group = {"ctime": None,
966
             "name": "default",
967
             }
968
    self.rapi.AddResponse(serializer.DumpJson(group))
969
    self.assertEqual({"ctime": None, "name": "default"},
970
                     self.client.GetGroup("default"))
971
    self.assertHandler(rlib2.R_2_groups_name)
972
    self.assertItems(["default"])
973

    
974
  def testCreateGroup(self):
975
    self.rapi.AddResponse("12345")
976
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
977
    self.assertEqual(job_id, 12345)
978
    self.assertHandler(rlib2.R_2_groups)
979
    self.assertDryRun()
980

    
981
  def testDeleteGroup(self):
982
    self.rapi.AddResponse("12346")
983
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
984
    self.assertEqual(job_id, 12346)
985
    self.assertHandler(rlib2.R_2_groups_name)
986
    self.assertDryRun()
987

    
988
  def testRenameGroup(self):
989
    self.rapi.AddResponse("12347")
990
    job_id = self.client.RenameGroup("oldname", "newname")
991
    self.assertEqual(job_id, 12347)
992
    self.assertHandler(rlib2.R_2_groups_name_rename)
993

    
994
  def testModifyGroup(self):
995
    self.rapi.AddResponse("12348")
996
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
997
    self.assertEqual(job_id, 12348)
998
    self.assertHandler(rlib2.R_2_groups_name_modify)
999

    
1000
  def testAssignGroupNodes(self):
1001
    self.rapi.AddResponse("12349")
1002
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1003
                                          force=True, dry_run=True)
1004
    self.assertEqual(job_id, 12349)
1005
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1006
    self.assertDryRun()
1007
    self.assertUseForce()
1008

    
1009
  def testModifyInstance(self):
1010
    self.rapi.AddResponse("23681")
1011
    job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1012
    self.assertEqual(job_id, 23681)
1013
    self.assertItems(["inst7210"])
1014
    self.assertHandler(rlib2.R_2_instances_name_modify)
1015
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1016
                     { "os_name": "linux", })
1017

    
1018
  def testModifyCluster(self):
1019
    for mnh in [None, False, True]:
1020
      self.rapi.AddResponse("14470")
1021
      self.assertEqual(14470,
1022
        self.client.ModifyCluster(maintain_node_health=mnh))
1023
      self.assertHandler(rlib2.R_2_cluster_modify)
1024
      self.assertItems([])
1025
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1026
      self.assertEqual(len(data), 1)
1027
      self.assertEqual(data["maintain_node_health"], mnh)
1028
      self.assertEqual(self.rapi.CountPending(), 0)
1029

    
1030
  def testRedistributeConfig(self):
1031
    self.rapi.AddResponse("3364")
1032
    job_id = self.client.RedistributeConfig()
1033
    self.assertEqual(job_id, 3364)
1034
    self.assertItems([])
1035
    self.assertHandler(rlib2.R_2_redist_config)
1036

    
1037
  def testActivateInstanceDisks(self):
1038
    self.rapi.AddResponse("23547")
1039
    job_id = self.client.ActivateInstanceDisks("inst28204")
1040
    self.assertEqual(job_id, 23547)
1041
    self.assertItems(["inst28204"])
1042
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1043
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1044

    
1045
  def testActivateInstanceDisksIgnoreSize(self):
1046
    self.rapi.AddResponse("11044")
1047
    job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1048
    self.assertEqual(job_id, 11044)
1049
    self.assertItems(["inst28204"])
1050
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1051
    self.assertQuery("ignore_size", ["1"])
1052

    
1053
  def testDeactivateInstanceDisks(self):
1054
    self.rapi.AddResponse("14591")
1055
    job_id = self.client.DeactivateInstanceDisks("inst28234")
1056
    self.assertEqual(job_id, 14591)
1057
    self.assertItems(["inst28234"])
1058
    self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1059
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1060

    
1061
  def testGetInstanceConsole(self):
1062
    self.rapi.AddResponse("26876")
1063
    job_id = self.client.GetInstanceConsole("inst21491")
1064
    self.assertEqual(job_id, 26876)
1065
    self.assertItems(["inst21491"])
1066
    self.assertHandler(rlib2.R_2_instances_name_console)
1067
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1068
    self.assertFalse(self.rapi.GetLastRequestData())
1069

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

    
1090
  def testQuery(self):
1091
    for idx, what in enumerate(constants.QR_VIA_RAPI):
1092
      for idx2, filter_ in enumerate([None, ["?", "name"]]):
1093
        job_id = 11010 + (idx << 4) + (idx2 << 16)
1094
        fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1095

    
1096
        self.rapi.AddResponse(str(job_id))
1097
        self.assertEqual(self.client.Query(what, fields, filter_=filter_),
1098
                         job_id)
1099
        self.assertItems([what])
1100
        self.assertHandler(rlib2.R_2_query)
1101
        self.assertFalse(self.rapi.GetLastHandler().queryargs)
1102
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
1103
        self.assertEqual(data["fields"], fields)
1104
        if filter_ is None:
1105
          self.assertTrue("filter" not in data)
1106
        else:
1107
          self.assertEqual(data["filter"], filter_)
1108
        self.assertEqual(self.rapi.CountPending(), 0)
1109

    
1110
  def testQueryFields(self):
1111
    exp_result = objects.QueryFieldsResponse(fields=[
1112
      objects.QueryFieldDefinition(name="pnode", title="PNode",
1113
                                   kind=constants.QFT_NUMBER),
1114
      objects.QueryFieldDefinition(name="other", title="Other",
1115
                                   kind=constants.QFT_BOOL),
1116
      ])
1117

    
1118
    for what in constants.QR_VIA_RAPI:
1119
      for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1120
        self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1121
        result = self.client.QueryFields(what, fields=fields)
1122
        self.assertItems([what])
1123
        self.assertHandler(rlib2.R_2_query_fields)
1124
        self.assertFalse(self.rapi.GetLastRequestData())
1125

    
1126
        queryargs = self.rapi.GetLastHandler().queryargs
1127
        if fields is None:
1128
          self.assertFalse(queryargs)
1129
        else:
1130
          self.assertEqual(queryargs, {
1131
            "fields": [",".join(fields)],
1132
            })
1133

    
1134
        self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1135
                         exp_result.ToDict())
1136

    
1137
        self.assertEqual(self.rapi.CountPending(), 0)
1138

    
1139
  def testWaitForJobCompletionNoChange(self):
1140
    resp = serializer.DumpJson({
1141
      "status": constants.JOB_STATUS_WAITLOCK,
1142
      })
1143

    
1144
    for retries in [1, 5, 25]:
1145
      for _ in range(retries):
1146
        self.rapi.AddResponse(resp)
1147

    
1148
      self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1149
                                                        retries=retries))
1150
      self.assertHandler(rlib2.R_2_jobs_id)
1151
      self.assertItems(["22789"])
1152

    
1153
      self.assertEqual(self.rapi.CountPending(), 0)
1154

    
1155
  def testWaitForJobCompletionAlreadyFinished(self):
1156
    self.rapi.AddResponse(serializer.DumpJson({
1157
      "status": constants.JOB_STATUS_SUCCESS,
1158
      }))
1159

    
1160
    self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1161
                                                     retries=1))
1162
    self.assertHandler(rlib2.R_2_jobs_id)
1163
    self.assertItems(["22793"])
1164

    
1165
    self.assertEqual(self.rapi.CountPending(), 0)
1166

    
1167
  def testWaitForJobCompletionEmptyResponse(self):
1168
    self.rapi.AddResponse("{}")
1169
    self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1170
                                                     retries=10))
1171
    self.assertHandler(rlib2.R_2_jobs_id)
1172
    self.assertItems(["22793"])
1173

    
1174
    self.assertEqual(self.rapi.CountPending(), 0)
1175

    
1176
  def testWaitForJobCompletionOutOfRetries(self):
1177
    for retries in [3, 10, 21]:
1178
      for _ in range(retries):
1179
        self.rapi.AddResponse(serializer.DumpJson({
1180
          "status": constants.JOB_STATUS_RUNNING,
1181
          }))
1182

    
1183
      self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1184
                                                        retries=retries - 1))
1185
      self.assertHandler(rlib2.R_2_jobs_id)
1186
      self.assertItems(["30948"])
1187

    
1188
      self.assertEqual(self.rapi.CountPending(), 1)
1189
      self.rapi.ResetResponses()
1190

    
1191
  def testWaitForJobCompletionSuccessAndFailure(self):
1192
    for retries in [1, 4, 13]:
1193
      for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1194
                                    (True, constants.JOB_STATUS_SUCCESS)]:
1195
        for _ in range(retries):
1196
          self.rapi.AddResponse(serializer.DumpJson({
1197
            "status": constants.JOB_STATUS_RUNNING,
1198
            }))
1199

    
1200
        self.rapi.AddResponse(serializer.DumpJson({
1201
          "status": end_status,
1202
          }))
1203

    
1204
        result = self.client.WaitForJobCompletion(3187, period=None,
1205
                                                  retries=retries + 1)
1206
        self.assertEqual(result, success)
1207
        self.assertHandler(rlib2.R_2_jobs_id)
1208
        self.assertItems(["3187"])
1209

    
1210
        self.assertEqual(self.rapi.CountPending(), 0)
1211

    
1212

    
1213
class RapiTestRunner(unittest.TextTestRunner):
1214
  def run(self, *args):
1215
    global _used_handlers
1216
    assert _used_handlers is None
1217

    
1218
    _used_handlers = set()
1219
    try:
1220
      # Run actual tests
1221
      result = unittest.TextTestRunner.run(self, *args)
1222

    
1223
      diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1224
             _KNOWN_UNUSED)
1225
      if diff:
1226
        raise AssertionError("The following RAPI resources were not used by the"
1227
                             " RAPI client: %r" % utils.CommaJoin(diff))
1228
    finally:
1229
      # Reset global variable
1230
      _used_handlers = None
1231

    
1232
    return result
1233

    
1234

    
1235
if __name__ == '__main__':
1236
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)