Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 0fa753ba

History | View | Annotate | Download (50.8 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._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
155
    self.assertEqual(client._NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
156
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
157
    self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
158
    self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
159
    self.assertEqual(client.JOB_STATUS_CANCELING,
160
                     constants.JOB_STATUS_CANCELING)
161
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
162
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
163
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
164
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
165
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
166
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
167

    
168
    # Node evacuation
169
    self.assertEqual(client.NODE_EVAC_PRI, constants.IALLOCATOR_NEVAC_PRI)
170
    self.assertEqual(client.NODE_EVAC_SEC, constants.IALLOCATOR_NEVAC_SEC)
171
    self.assertEqual(client.NODE_EVAC_ALL, constants.IALLOCATOR_NEVAC_ALL)
172

    
173
    # Legacy name
174
    self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITING)
175

    
176

    
177
class RapiMockTest(unittest.TestCase):
178
  def test(self):
179
    rapi = RapiMock()
180
    path = "/version"
181
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
182
    self.assertEqual((501, "Method not implemented"),
183
                     rapi.FetchResponse("/version", "POST", None))
184
    rapi.AddResponse("2")
185
    code, response = rapi.FetchResponse("/version", "GET", None)
186
    self.assertEqual(200, code)
187
    self.assertEqual("2", response)
188
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
189

    
190

    
191
def _FakeNoSslPycurlVersion():
192
  # Note: incomplete version tuple
193
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
194

    
195

    
196
def _FakeFancySslPycurlVersion():
197
  # Note: incomplete version tuple
198
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
199

    
200

    
201
def _FakeOpenSslPycurlVersion():
202
  # Note: incomplete version tuple
203
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
204

    
205

    
206
def _FakeGnuTlsPycurlVersion():
207
  # Note: incomplete version tuple
208
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
209

    
210

    
211
class TestExtendedConfig(unittest.TestCase):
212
  def testAuth(self):
213
    cl = client.GanetiRapiClient("master.example.com",
214
                                 username="user", password="pw",
215
                                 curl_factory=lambda: FakeCurl(RapiMock()))
216

    
217
    curl = cl._CreateCurl()
218
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
219
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
220

    
221
  def testInvalidAuth(self):
222
    # No username
223
    self.assertRaises(client.Error, client.GanetiRapiClient,
224
                      "master-a.example.com", password="pw")
225
    # No password
226
    self.assertRaises(client.Error, client.GanetiRapiClient,
227
                      "master-b.example.com", username="user")
228

    
229
  def testCertVerifyInvalidCombinations(self):
230
    self.assertRaises(client.Error, client.GenericCurlConfig,
231
                      use_curl_cabundle=True, cafile="cert1.pem")
232
    self.assertRaises(client.Error, client.GenericCurlConfig,
233
                      use_curl_cabundle=True, capath="certs/")
234
    self.assertRaises(client.Error, client.GenericCurlConfig,
235
                      use_curl_cabundle=True,
236
                      cafile="cert1.pem", capath="certs/")
237

    
238
  def testProxySignalVerifyHostname(self):
239
    for use_gnutls in [False, True]:
240
      if use_gnutls:
241
        pcverfn = _FakeGnuTlsPycurlVersion
242
      else:
243
        pcverfn = _FakeOpenSslPycurlVersion
244

    
245
      for proxy in ["", "http://127.0.0.1:1234"]:
246
        for use_signal in [False, True]:
247
          for verify_hostname in [False, True]:
248
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
249
                                             verify_hostname=verify_hostname,
250
                                             _pycurl_version_fn=pcverfn)
251

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

    
257
            curl = cl._CreateCurl()
258
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
259
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
260

    
261
            if verify_hostname:
262
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
263
            else:
264
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
265

    
266
  def testNoCertVerify(self):
267
    cfgfn = client.GenericCurlConfig()
268

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

    
273
    curl = cl._CreateCurl()
274
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
275
    self.assertFalse(curl.getopt(pycurl.CAINFO))
276
    self.assertFalse(curl.getopt(pycurl.CAPATH))
277

    
278
  def testCertVerifyCurlBundle(self):
279
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
280

    
281
    curl_factory = lambda: FakeCurl(RapiMock())
282
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
283
                                 curl_factory=curl_factory)
284

    
285
    curl = cl._CreateCurl()
286
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
287
    self.assertFalse(curl.getopt(pycurl.CAINFO))
288
    self.assertFalse(curl.getopt(pycurl.CAPATH))
289

    
290
  def testCertVerifyCafile(self):
291
    mycert = "/tmp/some/UNUSED/cert/file.pem"
292
    cfgfn = client.GenericCurlConfig(cafile=mycert)
293

    
294
    curl_factory = lambda: FakeCurl(RapiMock())
295
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
296
                                 curl_factory=curl_factory)
297

    
298
    curl = cl._CreateCurl()
299
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
300
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
301
    self.assertFalse(curl.getopt(pycurl.CAPATH))
302

    
303
  def testCertVerifyCapath(self):
304
    certdir = "/tmp/some/UNUSED/cert/directory"
305
    pcverfn = _FakeOpenSslPycurlVersion
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
    curl = cl._CreateCurl()
314
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
315
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
316
    self.assertFalse(curl.getopt(pycurl.CAINFO))
317

    
318
  def testCertVerifyCapathGnuTls(self):
319
    certdir = "/tmp/some/UNUSED/cert/directory"
320
    pcverfn = _FakeGnuTlsPycurlVersion
321
    cfgfn = client.GenericCurlConfig(capath=certdir,
322
                                     _pycurl_version_fn=pcverfn)
323

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

    
328
    self.assertRaises(client.Error, cl._CreateCurl)
329

    
330
  def testCertVerifyNoSsl(self):
331
    certdir = "/tmp/some/UNUSED/cert/directory"
332
    pcverfn = _FakeNoSslPycurlVersion
333
    cfgfn = client.GenericCurlConfig(capath=certdir,
334
                                     _pycurl_version_fn=pcverfn)
335

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

    
340
    self.assertRaises(client.Error, cl._CreateCurl)
341

    
342
  def testCertVerifyFancySsl(self):
343
    certdir = "/tmp/some/UNUSED/cert/directory"
344
    pcverfn = _FakeFancySslPycurlVersion
345
    cfgfn = client.GenericCurlConfig(capath=certdir,
346
                                     _pycurl_version_fn=pcverfn)
347

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

    
352
    self.assertRaises(NotImplementedError, cl._CreateCurl)
353

    
354
  def testCertVerifyCapath(self):
355
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
356
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
357
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
358
                                         timeout=timeout)
359

    
360
        curl_factory = lambda: FakeCurl(RapiMock())
361
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
362
                                     curl_factory=curl_factory)
363

    
364
        curl = cl._CreateCurl()
365
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
366
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
367

    
368

    
369
class GanetiRapiClientTests(testutils.GanetiTestCase):
370
  def setUp(self):
371
    testutils.GanetiTestCase.setUp(self)
372

    
373
    self.rapi = RapiMock()
374
    self.curl = FakeCurl(self.rapi)
375
    self.client = client.GanetiRapiClient("master.example.com",
376
                                          curl_factory=lambda: self.curl)
377

    
378
  def assertHandler(self, handler_cls):
379
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
380

    
381
  def assertQuery(self, key, value):
382
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
383

    
384
  def assertItems(self, items):
385
    self.assertEqual(items, self.rapi.GetLastHandler().items)
386

    
387
  def assertBulk(self):
388
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
389

    
390
  def assertDryRun(self):
391
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
392

    
393
  def assertUseForce(self):
394
    self.assertTrue(self.rapi.GetLastHandler().useForce())
395

    
396
  def testEncodeQuery(self):
397
    query = [
398
      ("a", None),
399
      ("b", 1),
400
      ("c", 2),
401
      ("d", "Foo"),
402
      ("e", True),
403
      ]
404

    
405
    expected = [
406
      ("a", ""),
407
      ("b", 1),
408
      ("c", 2),
409
      ("d", "Foo"),
410
      ("e", 1),
411
      ]
412

    
413
    self.assertEqualValues(self.client._EncodeQuery(query),
414
                           expected)
415

    
416
    # invalid types
417
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
418
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
419

    
420
  def testCurlSettings(self):
421
    self.rapi.AddResponse("2")
422
    self.assertEqual(2, self.client.GetVersion())
423
    self.assertHandler(rlib2.R_version)
424

    
425
    # Signals should be disabled by default
426
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
427

    
428
    # No auth and no proxy
429
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
430
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
431

    
432
    # Content-type is required for requests
433
    headers = self.curl.getopt(pycurl.HTTPHEADER)
434
    self.assert_("Content-type: application/json" in headers)
435

    
436
  def testHttpError(self):
437
    self.rapi.AddResponse(None, code=404)
438
    try:
439
      self.client.GetJobStatus(15140)
440
    except client.GanetiApiError, err:
441
      self.assertEqual(err.code, 404)
442
    else:
443
      self.fail("Didn't raise exception")
444

    
445
  def testGetVersion(self):
446
    self.rapi.AddResponse("2")
447
    self.assertEqual(2, self.client.GetVersion())
448
    self.assertHandler(rlib2.R_version)
449

    
450
  def testGetFeatures(self):
451
    for features in [[], ["foo", "bar", "baz"]]:
452
      self.rapi.AddResponse(serializer.DumpJson(features))
453
      self.assertEqual(features, self.client.GetFeatures())
454
      self.assertHandler(rlib2.R_2_features)
455

    
456
  def testGetFeaturesNotFound(self):
457
    self.rapi.AddResponse(None, code=404)
458
    self.assertEqual([], self.client.GetFeatures())
459

    
460
  def testGetOperatingSystems(self):
461
    self.rapi.AddResponse("[\"beos\"]")
462
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
463
    self.assertHandler(rlib2.R_2_os)
464

    
465
  def testGetClusterTags(self):
466
    self.rapi.AddResponse("[\"tag\"]")
467
    self.assertEqual(["tag"], self.client.GetClusterTags())
468
    self.assertHandler(rlib2.R_2_tags)
469

    
470
  def testAddClusterTags(self):
471
    self.rapi.AddResponse("1234")
472
    self.assertEqual(1234,
473
        self.client.AddClusterTags(["awesome"], dry_run=True))
474
    self.assertHandler(rlib2.R_2_tags)
475
    self.assertDryRun()
476
    self.assertQuery("tag", ["awesome"])
477

    
478
  def testDeleteClusterTags(self):
479
    self.rapi.AddResponse("5107")
480
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
481
                                                         dry_run=True))
482
    self.assertHandler(rlib2.R_2_tags)
483
    self.assertDryRun()
484
    self.assertQuery("tag", ["awesome"])
485

    
486
  def testGetInfo(self):
487
    self.rapi.AddResponse("{}")
488
    self.assertEqual({}, self.client.GetInfo())
489
    self.assertHandler(rlib2.R_2_info)
490

    
491
  def testGetInstances(self):
492
    self.rapi.AddResponse("[]")
493
    self.assertEqual([], self.client.GetInstances(bulk=True))
494
    self.assertHandler(rlib2.R_2_instances)
495
    self.assertBulk()
496

    
497
  def testGetInstance(self):
498
    self.rapi.AddResponse("[]")
499
    self.assertEqual([], self.client.GetInstance("instance"))
500
    self.assertHandler(rlib2.R_2_instances_name)
501
    self.assertItems(["instance"])
502

    
503
  def testGetInstanceInfo(self):
504
    self.rapi.AddResponse("21291")
505
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
506
    self.assertHandler(rlib2.R_2_instances_name_info)
507
    self.assertItems(["inst3"])
508
    self.assertQuery("static", None)
509

    
510
    self.rapi.AddResponse("3428")
511
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
512
    self.assertHandler(rlib2.R_2_instances_name_info)
513
    self.assertItems(["inst31"])
514
    self.assertQuery("static", ["0"])
515

    
516
    self.rapi.AddResponse("15665")
517
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
518
    self.assertHandler(rlib2.R_2_instances_name_info)
519
    self.assertItems(["inst32"])
520
    self.assertQuery("static", ["1"])
521

    
522
  def testCreateInstanceOldVersion(self):
523
    # The old request format, version 0, is no longer supported
524
    self.rapi.AddResponse(None, code=404)
525
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
526
                      "create", "inst1.example.com", "plain", [], [])
527
    self.assertEqual(self.rapi.CountPending(), 0)
528

    
529
  def testCreateInstance(self):
530
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
531
    self.rapi.AddResponse("23030")
532
    job_id = self.client.CreateInstance("create", "inst1.example.com",
533
                                        "plain", [], [], dry_run=True)
534
    self.assertEqual(job_id, 23030)
535
    self.assertHandler(rlib2.R_2_instances)
536
    self.assertDryRun()
537

    
538
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
539

    
540
    for field in ["dry_run", "beparams", "hvparams", "start"]:
541
      self.assertFalse(field in data)
542

    
543
    self.assertEqual(data["name"], "inst1.example.com")
544
    self.assertEqual(data["disk_template"], "plain")
545

    
546
  def testCreateInstance2(self):
547
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
548
    self.rapi.AddResponse("24740")
549
    job_id = self.client.CreateInstance("import", "inst2.example.com",
550
                                        "drbd8", [{"size": 100,}],
551
                                        [{}, {"bridge": "br1", }],
552
                                        dry_run=False, start=True,
553
                                        pnode="node1", snode="node9",
554
                                        ip_check=False)
555
    self.assertEqual(job_id, 24740)
556
    self.assertHandler(rlib2.R_2_instances)
557

    
558
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
559
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
560
    self.assertEqual(data["name"], "inst2.example.com")
561
    self.assertEqual(data["disk_template"], "drbd8")
562
    self.assertEqual(data["start"], True)
563
    self.assertEqual(data["ip_check"], False)
564
    self.assertEqualValues(data["disks"], [{"size": 100,}])
565
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
566

    
567
  def testDeleteInstance(self):
568
    self.rapi.AddResponse("1234")
569
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
570
    self.assertHandler(rlib2.R_2_instances_name)
571
    self.assertItems(["instance"])
572
    self.assertDryRun()
573

    
574
  def testGetInstanceTags(self):
575
    self.rapi.AddResponse("[]")
576
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
577
    self.assertHandler(rlib2.R_2_instances_name_tags)
578
    self.assertItems(["fooinstance"])
579

    
580
  def testAddInstanceTags(self):
581
    self.rapi.AddResponse("1234")
582
    self.assertEqual(1234,
583
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
584
    self.assertHandler(rlib2.R_2_instances_name_tags)
585
    self.assertItems(["fooinstance"])
586
    self.assertDryRun()
587
    self.assertQuery("tag", ["awesome"])
588

    
589
  def testDeleteInstanceTags(self):
590
    self.rapi.AddResponse("25826")
591
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
592
                                                           dry_run=True))
593
    self.assertHandler(rlib2.R_2_instances_name_tags)
594
    self.assertItems(["foo"])
595
    self.assertDryRun()
596
    self.assertQuery("tag", ["awesome"])
597

    
598
  def testRebootInstance(self):
599
    self.rapi.AddResponse("6146")
600
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
601
                                        ignore_secondaries=True, dry_run=True)
602
    self.assertEqual(6146, job_id)
603
    self.assertHandler(rlib2.R_2_instances_name_reboot)
604
    self.assertItems(["i-bar"])
605
    self.assertDryRun()
606
    self.assertQuery("type", ["hard"])
607
    self.assertQuery("ignore_secondaries", ["1"])
608

    
609
  def testShutdownInstance(self):
610
    self.rapi.AddResponse("1487")
611
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
612
                                                        dry_run=True))
613
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
614
    self.assertItems(["foo-instance"])
615
    self.assertDryRun()
616

    
617
  def testStartupInstance(self):
618
    self.rapi.AddResponse("27149")
619
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
620
                                                        dry_run=True))
621
    self.assertHandler(rlib2.R_2_instances_name_startup)
622
    self.assertItems(["bar-instance"])
623
    self.assertDryRun()
624

    
625
  def testReinstallInstance(self):
626
    self.rapi.AddResponse(serializer.DumpJson([]))
627
    self.rapi.AddResponse("19119")
628
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
629
                                                          os="DOS",
630
                                                          no_startup=True))
631
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
632
    self.assertItems(["baz-instance"])
633
    self.assertQuery("os", ["DOS"])
634
    self.assertQuery("nostartup", ["1"])
635
    self.assertEqual(self.rapi.CountPending(), 0)
636

    
637
  def testReinstallInstanceNew(self):
638
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
639
    self.rapi.AddResponse("25689")
640
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
641
                                                          os="Debian",
642
                                                          no_startup=True))
643
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
644
    self.assertItems(["moo-instance"])
645
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
646
    self.assertEqual(len(data), 2)
647
    self.assertEqual(data["os"], "Debian")
648
    self.assertEqual(data["start"], False)
649
    self.assertEqual(self.rapi.CountPending(), 0)
650

    
651
  def testReinstallInstanceWithOsparams1(self):
652
    self.rapi.AddResponse(serializer.DumpJson([]))
653
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
654
                      "doo-instance", osparams={"x": "y"})
655
    self.assertEqual(self.rapi.CountPending(), 0)
656

    
657
  def testReinstallInstanceWithOsparams2(self):
658
    osparams = {
659
      "Hello": "World",
660
      "foo": "bar",
661
      }
662
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
663
    self.rapi.AddResponse("1717")
664
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
665
                                                         osparams=osparams))
666
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
667
    self.assertItems(["zoo-instance"])
668
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
669
    self.assertEqual(len(data), 2)
670
    self.assertEqual(data["osparams"], osparams)
671
    self.assertEqual(data["start"], True)
672
    self.assertEqual(self.rapi.CountPending(), 0)
673

    
674
  def testReplaceInstanceDisks(self):
675
    self.rapi.AddResponse("999")
676
    job_id = self.client.ReplaceInstanceDisks("instance-name",
677
        disks=[0, 1], iallocator="hail")
678
    self.assertEqual(999, job_id)
679
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
680
    self.assertItems(["instance-name"])
681
    self.assertQuery("disks", ["0,1"])
682
    self.assertQuery("mode", ["replace_auto"])
683
    self.assertQuery("iallocator", ["hail"])
684

    
685
    self.rapi.AddResponse("1000")
686
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
687
        disks=[1], mode="replace_on_secondary", remote_node="foo-node")
688
    self.assertEqual(1000, job_id)
689
    self.assertItems(["instance-bar"])
690
    self.assertQuery("disks", ["1"])
691
    self.assertQuery("remote_node", ["foo-node"])
692

    
693
    self.rapi.AddResponse("5175")
694
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
695
    self.assertItems(["instance-moo"])
696
    self.assertQuery("disks", None)
697

    
698
  def testPrepareExport(self):
699
    self.rapi.AddResponse("8326")
700
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
701
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
702
    self.assertItems(["inst1"])
703
    self.assertQuery("mode", ["local"])
704

    
705
  def testExportInstance(self):
706
    self.rapi.AddResponse("19695")
707
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
708
                                        shutdown=True)
709
    self.assertEqual(job_id, 19695)
710
    self.assertHandler(rlib2.R_2_instances_name_export)
711
    self.assertItems(["inst2"])
712

    
713
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
714
    self.assertEqual(data["mode"], "local")
715
    self.assertEqual(data["destination"], "nodeX")
716
    self.assertEqual(data["shutdown"], True)
717

    
718
  def testMigrateInstanceDefaults(self):
719
    self.rapi.AddResponse("24873")
720
    job_id = self.client.MigrateInstance("inst91")
721
    self.assertEqual(job_id, 24873)
722
    self.assertHandler(rlib2.R_2_instances_name_migrate)
723
    self.assertItems(["inst91"])
724

    
725
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
726
    self.assertFalse(data)
727

    
728
  def testMigrateInstance(self):
729
    for mode in constants.HT_MIGRATION_MODES:
730
      for cleanup in [False, True]:
731
        self.rapi.AddResponse("31910")
732
        job_id = self.client.MigrateInstance("inst289", mode=mode,
733
                                             cleanup=cleanup)
734
        self.assertEqual(job_id, 31910)
735
        self.assertHandler(rlib2.R_2_instances_name_migrate)
736
        self.assertItems(["inst289"])
737

    
738
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
739
        self.assertEqual(len(data), 2)
740
        self.assertEqual(data["mode"], mode)
741
        self.assertEqual(data["cleanup"], cleanup)
742

    
743
  def testFailoverInstanceDefaults(self):
744
    self.rapi.AddResponse("7639")
745
    job_id = self.client.FailoverInstance("inst13579")
746
    self.assertEqual(job_id, 7639)
747
    self.assertHandler(rlib2.R_2_instances_name_failover)
748
    self.assertItems(["inst13579"])
749

    
750
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
751
    self.assertFalse(data)
752

    
753
  def testFailoverInstance(self):
754
    for iallocator in ["dumb", "hail"]:
755
      for ignore_consistency in [False, True]:
756
        for target_node in ["node-a", "node2"]:
757
          self.rapi.AddResponse("19161")
758
          job_id = \
759
            self.client.FailoverInstance("inst251", iallocator=iallocator,
760
                                         ignore_consistency=ignore_consistency,
761
                                         target_node=target_node)
762
          self.assertEqual(job_id, 19161)
763
          self.assertHandler(rlib2.R_2_instances_name_failover)
764
          self.assertItems(["inst251"])
765

    
766
          data = serializer.LoadJson(self.rapi.GetLastRequestData())
767
          self.assertEqual(len(data), 3)
768
          self.assertEqual(data["iallocator"], iallocator)
769
          self.assertEqual(data["ignore_consistency"], ignore_consistency)
770
          self.assertEqual(data["target_node"], target_node)
771
          self.assertEqual(self.rapi.CountPending(), 0)
772

    
773
  def testRenameInstanceDefaults(self):
774
    new_name = "newnametha7euqu"
775
    self.rapi.AddResponse("8791")
776
    job_id = self.client.RenameInstance("inst18821", new_name)
777
    self.assertEqual(job_id, 8791)
778
    self.assertHandler(rlib2.R_2_instances_name_rename)
779
    self.assertItems(["inst18821"])
780

    
781
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
782
    self.assertEqualValues(data, {"new_name": new_name, })
783

    
784
  def testRenameInstance(self):
785
    new_name = "new-name-yiux1iin"
786
    for ip_check in [False, True]:
787
      for name_check in [False, True]:
788
        self.rapi.AddResponse("24776")
789
        job_id = self.client.RenameInstance("inst20967", new_name,
790
                                             ip_check=ip_check,
791
                                             name_check=name_check)
792
        self.assertEqual(job_id, 24776)
793
        self.assertHandler(rlib2.R_2_instances_name_rename)
794
        self.assertItems(["inst20967"])
795

    
796
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
797
        self.assertEqual(len(data), 3)
798
        self.assertEqual(data["new_name"], new_name)
799
        self.assertEqual(data["ip_check"], ip_check)
800
        self.assertEqual(data["name_check"], name_check)
801

    
802
  def testGetJobs(self):
803
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
804
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
805
    self.assertEqual([123, 124], self.client.GetJobs())
806
    self.assertHandler(rlib2.R_2_jobs)
807

    
808
  def testGetJobStatus(self):
809
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
810
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
811
    self.assertHandler(rlib2.R_2_jobs_id)
812
    self.assertItems(["1234"])
813

    
814
  def testWaitForJobChange(self):
815
    fields = ["id", "summary"]
816
    expected = {
817
      "job_info": [123, "something"],
818
      "log_entries": [],
819
      }
820

    
821
    self.rapi.AddResponse(serializer.DumpJson(expected))
822
    result = self.client.WaitForJobChange(123, fields, [], -1)
823
    self.assertEqualValues(expected, result)
824
    self.assertHandler(rlib2.R_2_jobs_id_wait)
825
    self.assertItems(["123"])
826

    
827
  def testCancelJob(self):
828
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
829
    self.assertEqual([True, "Job 123 will be canceled"],
830
                     self.client.CancelJob(999, dry_run=True))
831
    self.assertHandler(rlib2.R_2_jobs_id)
832
    self.assertItems(["999"])
833
    self.assertDryRun()
834

    
835
  def testGetNodes(self):
836
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
837
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
838
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
839
    self.assertHandler(rlib2.R_2_nodes)
840

    
841
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
842
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
843
    self.assertEqual([{"id": "node1", "uri": "uri1"},
844
                      {"id": "node2", "uri": "uri2"}],
845
                     self.client.GetNodes(bulk=True))
846
    self.assertHandler(rlib2.R_2_nodes)
847
    self.assertBulk()
848

    
849
  def testGetNode(self):
850
    self.rapi.AddResponse("{}")
851
    self.assertEqual({}, self.client.GetNode("node-foo"))
852
    self.assertHandler(rlib2.R_2_nodes_name)
853
    self.assertItems(["node-foo"])
854

    
855
  def testEvacuateNode(self):
856
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
857
    self.rapi.AddResponse("9876")
858
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
859
    self.assertEqual(9876, job_id)
860
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
861
    self.assertItems(["node-1"])
862
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
863
                     { "remote_node": "node-2", })
864
    self.assertEqual(self.rapi.CountPending(), 0)
865

    
866
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
867
    self.rapi.AddResponse("8888")
868
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
869
                                      mode=constants.IALLOCATOR_NEVAC_ALL,
870
                                      early_release=True)
871
    self.assertEqual(8888, job_id)
872
    self.assertItems(["node-3"])
873
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
874
      "iallocator": "hail",
875
      "mode": "all",
876
      "early_release": True,
877
      })
878
    self.assertDryRun()
879

    
880
    self.assertRaises(client.GanetiApiError,
881
                      self.client.EvacuateNode,
882
                      "node-4", iallocator="hail", remote_node="node-5")
883
    self.assertEqual(self.rapi.CountPending(), 0)
884

    
885
  def testEvacuateNodeOldResponse(self):
886
    self.rapi.AddResponse(serializer.DumpJson([]))
887
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
888
                      "node-4", accept_old=False)
889
    self.assertEqual(self.rapi.CountPending(), 0)
890

    
891
    for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
892
      self.rapi.AddResponse(serializer.DumpJson([]))
893
      self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
894
                        "node-4", accept_old=True, mode=mode)
895
      self.assertEqual(self.rapi.CountPending(), 0)
896

    
897
    self.rapi.AddResponse(serializer.DumpJson([]))
898
    self.rapi.AddResponse(serializer.DumpJson("21533"))
899
    result = self.client.EvacuateNode("node-3", iallocator="hail",
900
                                      dry_run=True, accept_old=True,
901
                                      mode=client.NODE_EVAC_SEC,
902
                                      early_release=True)
903
    self.assertEqual(result, "21533")
904
    self.assertItems(["node-3"])
905
    self.assertQuery("iallocator", ["hail"])
906
    self.assertQuery("early_release", ["1"])
907
    self.assertFalse(self.rapi.GetLastRequestData())
908
    self.assertDryRun()
909
    self.assertEqual(self.rapi.CountPending(), 0)
910

    
911
  def testMigrateNode(self):
912
    self.rapi.AddResponse(serializer.DumpJson([]))
913
    self.rapi.AddResponse("1111")
914
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
915
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
916
    self.assertItems(["node-a"])
917
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
918
    self.assertDryRun()
919
    self.assertFalse(self.rapi.GetLastRequestData())
920

    
921
    self.rapi.AddResponse(serializer.DumpJson([]))
922
    self.rapi.AddResponse("1112")
923
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
924
                                                   mode="live"))
925
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
926
    self.assertItems(["node-a"])
927
    self.assertQuery("mode", ["live"])
928
    self.assertDryRun()
929
    self.assertFalse(self.rapi.GetLastRequestData())
930

    
931
    self.rapi.AddResponse(serializer.DumpJson([]))
932
    self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
933
                      "node-c", target_node="foonode")
934
    self.assertEqual(self.rapi.CountPending(), 0)
935

    
936
  def testMigrateNodeBodyData(self):
937
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
938
    self.rapi.AddResponse("27539")
939
    self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
940
                                                    mode="live"))
941
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
942
    self.assertItems(["node-a"])
943
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
944
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
945
                     { "mode": "live", })
946

    
947
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
948
    self.rapi.AddResponse("14219")
949
    self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
950
                                                    target_node="node9",
951
                                                    iallocator="ial"))
952
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
953
    self.assertItems(["node-x"])
954
    self.assertDryRun()
955
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
956
                     { "target_node": "node9", "iallocator": "ial", })
957

    
958
    self.assertEqual(self.rapi.CountPending(), 0)
959

    
960
  def testGetNodeRole(self):
961
    self.rapi.AddResponse("\"master\"")
962
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
963
    self.assertHandler(rlib2.R_2_nodes_name_role)
964
    self.assertItems(["node-a"])
965

    
966
  def testSetNodeRole(self):
967
    self.rapi.AddResponse("789")
968
    self.assertEqual(789,
969
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
970
    self.assertHandler(rlib2.R_2_nodes_name_role)
971
    self.assertItems(["node-foo"])
972
    self.assertQuery("force", ["1"])
973
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
974

    
975
  def testGetNodeStorageUnits(self):
976
    self.rapi.AddResponse("42")
977
    self.assertEqual(42,
978
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
979
    self.assertHandler(rlib2.R_2_nodes_name_storage)
980
    self.assertItems(["node-x"])
981
    self.assertQuery("storage_type", ["lvm-pv"])
982
    self.assertQuery("output_fields", ["fields"])
983

    
984
  def testModifyNodeStorageUnits(self):
985
    self.rapi.AddResponse("14")
986
    self.assertEqual(14,
987
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
988
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
989
    self.assertItems(["node-z"])
990
    self.assertQuery("storage_type", ["lvm-pv"])
991
    self.assertQuery("name", ["hda"])
992
    self.assertQuery("allocatable", None)
993

    
994
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
995
      self.rapi.AddResponse("7205")
996
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
997
                                                  allocatable=allocatable)
998
      self.assertEqual(7205, job_id)
999
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1000
      self.assertItems(["node-z"])
1001
      self.assertQuery("storage_type", ["lvm-pv"])
1002
      self.assertQuery("name", ["hda"])
1003
      self.assertQuery("allocatable", [query_allocatable])
1004

    
1005
  def testRepairNodeStorageUnits(self):
1006
    self.rapi.AddResponse("99")
1007
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1008
                                                            "hda"))
1009
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1010
    self.assertItems(["node-z"])
1011
    self.assertQuery("storage_type", ["lvm-pv"])
1012
    self.assertQuery("name", ["hda"])
1013

    
1014
  def testGetNodeTags(self):
1015
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
1016
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1017
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1018
    self.assertItems(["node-k"])
1019

    
1020
  def testAddNodeTags(self):
1021
    self.rapi.AddResponse("1234")
1022
    self.assertEqual(1234,
1023
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1024
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1025
    self.assertItems(["node-v"])
1026
    self.assertDryRun()
1027
    self.assertQuery("tag", ["awesome"])
1028

    
1029
  def testDeleteNodeTags(self):
1030
    self.rapi.AddResponse("16861")
1031
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1032
                                                       dry_run=True))
1033
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1034
    self.assertItems(["node-w"])
1035
    self.assertDryRun()
1036
    self.assertQuery("tag", ["awesome"])
1037

    
1038
  def testGetGroups(self):
1039
    groups = [{"name": "group1",
1040
               "uri": "/2/groups/group1",
1041
               },
1042
              {"name": "group2",
1043
               "uri": "/2/groups/group2",
1044
               },
1045
              ]
1046
    self.rapi.AddResponse(serializer.DumpJson(groups))
1047
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
1048
    self.assertHandler(rlib2.R_2_groups)
1049

    
1050
  def testGetGroupsBulk(self):
1051
    groups = [{"name": "group1",
1052
               "uri": "/2/groups/group1",
1053
               "node_cnt": 2,
1054
               "node_list": ["gnt1.test",
1055
                             "gnt2.test",
1056
                             ],
1057
               },
1058
              {"name": "group2",
1059
               "uri": "/2/groups/group2",
1060
               "node_cnt": 1,
1061
               "node_list": ["gnt3.test",
1062
                             ],
1063
               },
1064
              ]
1065
    self.rapi.AddResponse(serializer.DumpJson(groups))
1066

    
1067
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
1068
    self.assertHandler(rlib2.R_2_groups)
1069
    self.assertBulk()
1070

    
1071
  def testGetGroup(self):
1072
    group = {"ctime": None,
1073
             "name": "default",
1074
             }
1075
    self.rapi.AddResponse(serializer.DumpJson(group))
1076
    self.assertEqual({"ctime": None, "name": "default"},
1077
                     self.client.GetGroup("default"))
1078
    self.assertHandler(rlib2.R_2_groups_name)
1079
    self.assertItems(["default"])
1080

    
1081
  def testCreateGroup(self):
1082
    self.rapi.AddResponse("12345")
1083
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
1084
    self.assertEqual(job_id, 12345)
1085
    self.assertHandler(rlib2.R_2_groups)
1086
    self.assertDryRun()
1087

    
1088
  def testDeleteGroup(self):
1089
    self.rapi.AddResponse("12346")
1090
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1091
    self.assertEqual(job_id, 12346)
1092
    self.assertHandler(rlib2.R_2_groups_name)
1093
    self.assertDryRun()
1094

    
1095
  def testRenameGroup(self):
1096
    self.rapi.AddResponse("12347")
1097
    job_id = self.client.RenameGroup("oldname", "newname")
1098
    self.assertEqual(job_id, 12347)
1099
    self.assertHandler(rlib2.R_2_groups_name_rename)
1100

    
1101
  def testModifyGroup(self):
1102
    self.rapi.AddResponse("12348")
1103
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1104
    self.assertEqual(job_id, 12348)
1105
    self.assertHandler(rlib2.R_2_groups_name_modify)
1106

    
1107
  def testAssignGroupNodes(self):
1108
    self.rapi.AddResponse("12349")
1109
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1110
                                          force=True, dry_run=True)
1111
    self.assertEqual(job_id, 12349)
1112
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1113
    self.assertDryRun()
1114
    self.assertUseForce()
1115

    
1116
  def testModifyInstance(self):
1117
    self.rapi.AddResponse("23681")
1118
    job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1119
    self.assertEqual(job_id, 23681)
1120
    self.assertItems(["inst7210"])
1121
    self.assertHandler(rlib2.R_2_instances_name_modify)
1122
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1123
                     { "os_name": "linux", })
1124

    
1125
  def testModifyCluster(self):
1126
    for mnh in [None, False, True]:
1127
      self.rapi.AddResponse("14470")
1128
      self.assertEqual(14470,
1129
        self.client.ModifyCluster(maintain_node_health=mnh))
1130
      self.assertHandler(rlib2.R_2_cluster_modify)
1131
      self.assertItems([])
1132
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1133
      self.assertEqual(len(data), 1)
1134
      self.assertEqual(data["maintain_node_health"], mnh)
1135
      self.assertEqual(self.rapi.CountPending(), 0)
1136

    
1137
  def testRedistributeConfig(self):
1138
    self.rapi.AddResponse("3364")
1139
    job_id = self.client.RedistributeConfig()
1140
    self.assertEqual(job_id, 3364)
1141
    self.assertItems([])
1142
    self.assertHandler(rlib2.R_2_redist_config)
1143

    
1144
  def testActivateInstanceDisks(self):
1145
    self.rapi.AddResponse("23547")
1146
    job_id = self.client.ActivateInstanceDisks("inst28204")
1147
    self.assertEqual(job_id, 23547)
1148
    self.assertItems(["inst28204"])
1149
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1150
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1151

    
1152
  def testActivateInstanceDisksIgnoreSize(self):
1153
    self.rapi.AddResponse("11044")
1154
    job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1155
    self.assertEqual(job_id, 11044)
1156
    self.assertItems(["inst28204"])
1157
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1158
    self.assertQuery("ignore_size", ["1"])
1159

    
1160
  def testDeactivateInstanceDisks(self):
1161
    self.rapi.AddResponse("14591")
1162
    job_id = self.client.DeactivateInstanceDisks("inst28234")
1163
    self.assertEqual(job_id, 14591)
1164
    self.assertItems(["inst28234"])
1165
    self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1166
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1167

    
1168
  def testGetInstanceConsole(self):
1169
    self.rapi.AddResponse("26876")
1170
    job_id = self.client.GetInstanceConsole("inst21491")
1171
    self.assertEqual(job_id, 26876)
1172
    self.assertItems(["inst21491"])
1173
    self.assertHandler(rlib2.R_2_instances_name_console)
1174
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1175
    self.assertFalse(self.rapi.GetLastRequestData())
1176

    
1177
  def testGrowInstanceDisk(self):
1178
    for idx, wait_for_sync in enumerate([None, False, True]):
1179
      amount = 128 + (512 * idx)
1180
      self.assertEqual(self.rapi.CountPending(), 0)
1181
      self.rapi.AddResponse("30783")
1182
      self.assertEqual(30783,
1183
        self.client.GrowInstanceDisk("eze8ch", idx, amount,
1184
                                     wait_for_sync=wait_for_sync))
1185
      self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1186
      self.assertItems(["eze8ch", str(idx)])
1187
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1188
      if wait_for_sync is None:
1189
        self.assertEqual(len(data), 1)
1190
        self.assert_("wait_for_sync" not in data)
1191
      else:
1192
        self.assertEqual(len(data), 2)
1193
        self.assertEqual(data["wait_for_sync"], wait_for_sync)
1194
      self.assertEqual(data["amount"], amount)
1195
      self.assertEqual(self.rapi.CountPending(), 0)
1196

    
1197
  def testGetGroupTags(self):
1198
    self.rapi.AddResponse("[]")
1199
    self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1200
    self.assertHandler(rlib2.R_2_groups_name_tags)
1201
    self.assertItems(["fooGroup"])
1202

    
1203
  def testAddGroupTags(self):
1204
    self.rapi.AddResponse("1234")
1205
    self.assertEqual(1234,
1206
        self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1207
    self.assertHandler(rlib2.R_2_groups_name_tags)
1208
    self.assertItems(["fooGroup"])
1209
    self.assertDryRun()
1210
    self.assertQuery("tag", ["awesome"])
1211

    
1212
  def testDeleteGroupTags(self):
1213
    self.rapi.AddResponse("25826")
1214
    self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1215
                                                        dry_run=True))
1216
    self.assertHandler(rlib2.R_2_groups_name_tags)
1217
    self.assertItems(["foo"])
1218
    self.assertDryRun()
1219
    self.assertQuery("tag", ["awesome"])
1220

    
1221
  def testQuery(self):
1222
    for idx, what in enumerate(constants.QR_VIA_RAPI):
1223
      for idx2, filter_ in enumerate([None, ["?", "name"]]):
1224
        job_id = 11010 + (idx << 4) + (idx2 << 16)
1225
        fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1226

    
1227
        self.rapi.AddResponse(str(job_id))
1228
        self.assertEqual(self.client.Query(what, fields, filter_=filter_),
1229
                         job_id)
1230
        self.assertItems([what])
1231
        self.assertHandler(rlib2.R_2_query)
1232
        self.assertFalse(self.rapi.GetLastHandler().queryargs)
1233
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
1234
        self.assertEqual(data["fields"], fields)
1235
        if filter_ is None:
1236
          self.assertTrue("filter" not in data)
1237
        else:
1238
          self.assertEqual(data["filter"], filter_)
1239
        self.assertEqual(self.rapi.CountPending(), 0)
1240

    
1241
  def testQueryFields(self):
1242
    exp_result = objects.QueryFieldsResponse(fields=[
1243
      objects.QueryFieldDefinition(name="pnode", title="PNode",
1244
                                   kind=constants.QFT_NUMBER),
1245
      objects.QueryFieldDefinition(name="other", title="Other",
1246
                                   kind=constants.QFT_BOOL),
1247
      ])
1248

    
1249
    for what in constants.QR_VIA_RAPI:
1250
      for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1251
        self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1252
        result = self.client.QueryFields(what, fields=fields)
1253
        self.assertItems([what])
1254
        self.assertHandler(rlib2.R_2_query_fields)
1255
        self.assertFalse(self.rapi.GetLastRequestData())
1256

    
1257
        queryargs = self.rapi.GetLastHandler().queryargs
1258
        if fields is None:
1259
          self.assertFalse(queryargs)
1260
        else:
1261
          self.assertEqual(queryargs, {
1262
            "fields": [",".join(fields)],
1263
            })
1264

    
1265
        self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1266
                         exp_result.ToDict())
1267

    
1268
        self.assertEqual(self.rapi.CountPending(), 0)
1269

    
1270
  def testWaitForJobCompletionNoChange(self):
1271
    resp = serializer.DumpJson({
1272
      "status": constants.JOB_STATUS_WAITING,
1273
      })
1274

    
1275
    for retries in [1, 5, 25]:
1276
      for _ in range(retries):
1277
        self.rapi.AddResponse(resp)
1278

    
1279
      self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1280
                                                        retries=retries))
1281
      self.assertHandler(rlib2.R_2_jobs_id)
1282
      self.assertItems(["22789"])
1283

    
1284
      self.assertEqual(self.rapi.CountPending(), 0)
1285

    
1286
  def testWaitForJobCompletionAlreadyFinished(self):
1287
    self.rapi.AddResponse(serializer.DumpJson({
1288
      "status": constants.JOB_STATUS_SUCCESS,
1289
      }))
1290

    
1291
    self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1292
                                                     retries=1))
1293
    self.assertHandler(rlib2.R_2_jobs_id)
1294
    self.assertItems(["22793"])
1295

    
1296
    self.assertEqual(self.rapi.CountPending(), 0)
1297

    
1298
  def testWaitForJobCompletionEmptyResponse(self):
1299
    self.rapi.AddResponse("{}")
1300
    self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1301
                                                     retries=10))
1302
    self.assertHandler(rlib2.R_2_jobs_id)
1303
    self.assertItems(["22793"])
1304

    
1305
    self.assertEqual(self.rapi.CountPending(), 0)
1306

    
1307
  def testWaitForJobCompletionOutOfRetries(self):
1308
    for retries in [3, 10, 21]:
1309
      for _ in range(retries):
1310
        self.rapi.AddResponse(serializer.DumpJson({
1311
          "status": constants.JOB_STATUS_RUNNING,
1312
          }))
1313

    
1314
      self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1315
                                                        retries=retries - 1))
1316
      self.assertHandler(rlib2.R_2_jobs_id)
1317
      self.assertItems(["30948"])
1318

    
1319
      self.assertEqual(self.rapi.CountPending(), 1)
1320
      self.rapi.ResetResponses()
1321

    
1322
  def testWaitForJobCompletionSuccessAndFailure(self):
1323
    for retries in [1, 4, 13]:
1324
      for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1325
                                    (True, constants.JOB_STATUS_SUCCESS)]:
1326
        for _ in range(retries):
1327
          self.rapi.AddResponse(serializer.DumpJson({
1328
            "status": constants.JOB_STATUS_RUNNING,
1329
            }))
1330

    
1331
        self.rapi.AddResponse(serializer.DumpJson({
1332
          "status": end_status,
1333
          }))
1334

    
1335
        result = self.client.WaitForJobCompletion(3187, period=None,
1336
                                                  retries=retries + 1)
1337
        self.assertEqual(result, success)
1338
        self.assertHandler(rlib2.R_2_jobs_id)
1339
        self.assertItems(["3187"])
1340

    
1341
        self.assertEqual(self.rapi.CountPending(), 0)
1342

    
1343

    
1344
class RapiTestRunner(unittest.TextTestRunner):
1345
  def run(self, *args):
1346
    global _used_handlers
1347
    assert _used_handlers is None
1348

    
1349
    _used_handlers = set()
1350
    try:
1351
      # Run actual tests
1352
      result = unittest.TextTestRunner.run(self, *args)
1353

    
1354
      diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1355
             _KNOWN_UNUSED)
1356
      if diff:
1357
        raise AssertionError("The following RAPI resources were not used by the"
1358
                             " RAPI client: %r" % utils.CommaJoin(diff))
1359
    finally:
1360
      # Reset global variable
1361
      _used_handlers = None
1362

    
1363
    return result
1364

    
1365

    
1366
if __name__ == '__main__':
1367
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)