Statistics
| Branch: | Tag: | Revision:

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

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
  rlib2.R_root,
49
  ])
50

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

    
54

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

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

    
65

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

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

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

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

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

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

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

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

    
97

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

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

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

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

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

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

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

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

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

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

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

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

    
142
    return code, response
143

    
144

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

    
167
    # Legacy name
168
    self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITING)
169

    
170

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

    
184

    
185
def _FakeNoSslPycurlVersion():
186
  # Note: incomplete version tuple
187
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
188

    
189

    
190
def _FakeFancySslPycurlVersion():
191
  # Note: incomplete version tuple
192
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
193

    
194

    
195
def _FakeOpenSslPycurlVersion():
196
  # Note: incomplete version tuple
197
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
198

    
199

    
200
def _FakeGnuTlsPycurlVersion():
201
  # Note: incomplete version tuple
202
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
203

    
204

    
205
class TestExtendedConfig(unittest.TestCase):
206
  def testAuth(self):
207
    cl = client.GanetiRapiClient("master.example.com",
208
                                 username="user", password="pw",
209
                                 curl_factory=lambda: FakeCurl(RapiMock()))
210

    
211
    curl = cl._CreateCurl()
212
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
213
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
214

    
215
  def testInvalidAuth(self):
216
    # No username
217
    self.assertRaises(client.Error, client.GanetiRapiClient,
218
                      "master-a.example.com", password="pw")
219
    # No password
220
    self.assertRaises(client.Error, client.GanetiRapiClient,
221
                      "master-b.example.com", username="user")
222

    
223
  def testCertVerifyInvalidCombinations(self):
224
    self.assertRaises(client.Error, client.GenericCurlConfig,
225
                      use_curl_cabundle=True, cafile="cert1.pem")
226
    self.assertRaises(client.Error, client.GenericCurlConfig,
227
                      use_curl_cabundle=True, capath="certs/")
228
    self.assertRaises(client.Error, client.GenericCurlConfig,
229
                      use_curl_cabundle=True,
230
                      cafile="cert1.pem", capath="certs/")
231

    
232
  def testProxySignalVerifyHostname(self):
233
    for use_gnutls in [False, True]:
234
      if use_gnutls:
235
        pcverfn = _FakeGnuTlsPycurlVersion
236
      else:
237
        pcverfn = _FakeOpenSslPycurlVersion
238

    
239
      for proxy in ["", "http://127.0.0.1:1234"]:
240
        for use_signal in [False, True]:
241
          for verify_hostname in [False, True]:
242
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
243
                                             verify_hostname=verify_hostname,
244
                                             _pycurl_version_fn=pcverfn)
245

    
246
            curl_factory = lambda: FakeCurl(RapiMock())
247
            cl = client.GanetiRapiClient("master.example.com",
248
                                         curl_config_fn=cfgfn,
249
                                         curl_factory=curl_factory)
250

    
251
            curl = cl._CreateCurl()
252
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
253
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
254

    
255
            if verify_hostname:
256
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
257
            else:
258
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
259

    
260
  def testNoCertVerify(self):
261
    cfgfn = client.GenericCurlConfig()
262

    
263
    curl_factory = lambda: FakeCurl(RapiMock())
264
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
265
                                 curl_factory=curl_factory)
266

    
267
    curl = cl._CreateCurl()
268
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
269
    self.assertFalse(curl.getopt(pycurl.CAINFO))
270
    self.assertFalse(curl.getopt(pycurl.CAPATH))
271

    
272
  def testCertVerifyCurlBundle(self):
273
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
274

    
275
    curl_factory = lambda: FakeCurl(RapiMock())
276
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
277
                                 curl_factory=curl_factory)
278

    
279
    curl = cl._CreateCurl()
280
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
281
    self.assertFalse(curl.getopt(pycurl.CAINFO))
282
    self.assertFalse(curl.getopt(pycurl.CAPATH))
283

    
284
  def testCertVerifyCafile(self):
285
    mycert = "/tmp/some/UNUSED/cert/file.pem"
286
    cfgfn = client.GenericCurlConfig(cafile=mycert)
287

    
288
    curl_factory = lambda: FakeCurl(RapiMock())
289
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
290
                                 curl_factory=curl_factory)
291

    
292
    curl = cl._CreateCurl()
293
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
294
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
295
    self.assertFalse(curl.getopt(pycurl.CAPATH))
296

    
297
  def testCertVerifyCapath(self):
298
    certdir = "/tmp/some/UNUSED/cert/directory"
299
    pcverfn = _FakeOpenSslPycurlVersion
300
    cfgfn = client.GenericCurlConfig(capath=certdir,
301
                                     _pycurl_version_fn=pcverfn)
302

    
303
    curl_factory = lambda: FakeCurl(RapiMock())
304
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
305
                                 curl_factory=curl_factory)
306

    
307
    curl = cl._CreateCurl()
308
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
309
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
310
    self.assertFalse(curl.getopt(pycurl.CAINFO))
311

    
312
  def testCertVerifyCapathGnuTls(self):
313
    certdir = "/tmp/some/UNUSED/cert/directory"
314
    pcverfn = _FakeGnuTlsPycurlVersion
315
    cfgfn = client.GenericCurlConfig(capath=certdir,
316
                                     _pycurl_version_fn=pcverfn)
317

    
318
    curl_factory = lambda: FakeCurl(RapiMock())
319
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
320
                                 curl_factory=curl_factory)
321

    
322
    self.assertRaises(client.Error, cl._CreateCurl)
323

    
324
  def testCertVerifyNoSsl(self):
325
    certdir = "/tmp/some/UNUSED/cert/directory"
326
    pcverfn = _FakeNoSslPycurlVersion
327
    cfgfn = client.GenericCurlConfig(capath=certdir,
328
                                     _pycurl_version_fn=pcverfn)
329

    
330
    curl_factory = lambda: FakeCurl(RapiMock())
331
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
332
                                 curl_factory=curl_factory)
333

    
334
    self.assertRaises(client.Error, cl._CreateCurl)
335

    
336
  def testCertVerifyFancySsl(self):
337
    certdir = "/tmp/some/UNUSED/cert/directory"
338
    pcverfn = _FakeFancySslPycurlVersion
339
    cfgfn = client.GenericCurlConfig(capath=certdir,
340
                                     _pycurl_version_fn=pcverfn)
341

    
342
    curl_factory = lambda: FakeCurl(RapiMock())
343
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
344
                                 curl_factory=curl_factory)
345

    
346
    self.assertRaises(NotImplementedError, cl._CreateCurl)
347

    
348
  def testCertVerifyCapath(self):
349
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
350
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
351
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
352
                                         timeout=timeout)
353

    
354
        curl_factory = lambda: FakeCurl(RapiMock())
355
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
356
                                     curl_factory=curl_factory)
357

    
358
        curl = cl._CreateCurl()
359
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
360
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
361

    
362

    
363
class GanetiRapiClientTests(testutils.GanetiTestCase):
364
  def setUp(self):
365
    testutils.GanetiTestCase.setUp(self)
366

    
367
    self.rapi = RapiMock()
368
    self.curl = FakeCurl(self.rapi)
369
    self.client = client.GanetiRapiClient("master.example.com",
370
                                          curl_factory=lambda: self.curl)
371

    
372
  def assertHandler(self, handler_cls):
373
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
374

    
375
  def assertQuery(self, key, value):
376
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
377

    
378
  def assertItems(self, items):
379
    self.assertEqual(items, self.rapi.GetLastHandler().items)
380

    
381
  def assertBulk(self):
382
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
383

    
384
  def assertDryRun(self):
385
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
386

    
387
  def assertUseForce(self):
388
    self.assertTrue(self.rapi.GetLastHandler().useForce())
389

    
390
  def testEncodeQuery(self):
391
    query = [
392
      ("a", None),
393
      ("b", 1),
394
      ("c", 2),
395
      ("d", "Foo"),
396
      ("e", True),
397
      ]
398

    
399
    expected = [
400
      ("a", ""),
401
      ("b", 1),
402
      ("c", 2),
403
      ("d", "Foo"),
404
      ("e", 1),
405
      ]
406

    
407
    self.assertEqualValues(self.client._EncodeQuery(query),
408
                           expected)
409

    
410
    # invalid types
411
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
412
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
413

    
414
  def testCurlSettings(self):
415
    self.rapi.AddResponse("2")
416
    self.assertEqual(2, self.client.GetVersion())
417
    self.assertHandler(rlib2.R_version)
418

    
419
    # Signals should be disabled by default
420
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
421

    
422
    # No auth and no proxy
423
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
424
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
425

    
426
    # Content-type is required for requests
427
    headers = self.curl.getopt(pycurl.HTTPHEADER)
428
    self.assert_("Content-type: application/json" in headers)
429

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

    
439
  def testGetVersion(self):
440
    self.rapi.AddResponse("2")
441
    self.assertEqual(2, self.client.GetVersion())
442
    self.assertHandler(rlib2.R_version)
443

    
444
  def testGetFeatures(self):
445
    for features in [[], ["foo", "bar", "baz"]]:
446
      self.rapi.AddResponse(serializer.DumpJson(features))
447
      self.assertEqual(features, self.client.GetFeatures())
448
      self.assertHandler(rlib2.R_2_features)
449

    
450
  def testGetFeaturesNotFound(self):
451
    self.rapi.AddResponse(None, code=404)
452
    self.assertEqual([], self.client.GetFeatures())
453

    
454
  def testGetOperatingSystems(self):
455
    self.rapi.AddResponse("[\"beos\"]")
456
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
457
    self.assertHandler(rlib2.R_2_os)
458

    
459
  def testGetClusterTags(self):
460
    self.rapi.AddResponse("[\"tag\"]")
461
    self.assertEqual(["tag"], self.client.GetClusterTags())
462
    self.assertHandler(rlib2.R_2_tags)
463

    
464
  def testAddClusterTags(self):
465
    self.rapi.AddResponse("1234")
466
    self.assertEqual(1234,
467
        self.client.AddClusterTags(["awesome"], dry_run=True))
468
    self.assertHandler(rlib2.R_2_tags)
469
    self.assertDryRun()
470
    self.assertQuery("tag", ["awesome"])
471

    
472
  def testDeleteClusterTags(self):
473
    self.rapi.AddResponse("5107")
474
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
475
                                                         dry_run=True))
476
    self.assertHandler(rlib2.R_2_tags)
477
    self.assertDryRun()
478
    self.assertQuery("tag", ["awesome"])
479

    
480
  def testGetInfo(self):
481
    self.rapi.AddResponse("{}")
482
    self.assertEqual({}, self.client.GetInfo())
483
    self.assertHandler(rlib2.R_2_info)
484

    
485
  def testGetInstances(self):
486
    self.rapi.AddResponse("[]")
487
    self.assertEqual([], self.client.GetInstances(bulk=True))
488
    self.assertHandler(rlib2.R_2_instances)
489
    self.assertBulk()
490

    
491
  def testGetInstance(self):
492
    self.rapi.AddResponse("[]")
493
    self.assertEqual([], self.client.GetInstance("instance"))
494
    self.assertHandler(rlib2.R_2_instances_name)
495
    self.assertItems(["instance"])
496

    
497
  def testGetInstanceInfo(self):
498
    self.rapi.AddResponse("21291")
499
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
500
    self.assertHandler(rlib2.R_2_instances_name_info)
501
    self.assertItems(["inst3"])
502
    self.assertQuery("static", None)
503

    
504
    self.rapi.AddResponse("3428")
505
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
506
    self.assertHandler(rlib2.R_2_instances_name_info)
507
    self.assertItems(["inst31"])
508
    self.assertQuery("static", ["0"])
509

    
510
    self.rapi.AddResponse("15665")
511
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
512
    self.assertHandler(rlib2.R_2_instances_name_info)
513
    self.assertItems(["inst32"])
514
    self.assertQuery("static", ["1"])
515

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

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

    
532
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
533

    
534
    for field in ["dry_run", "beparams", "hvparams", "start"]:
535
      self.assertFalse(field in data)
536

    
537
    self.assertEqual(data["name"], "inst1.example.com")
538
    self.assertEqual(data["disk_template"], "plain")
539

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

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

    
561
  def testDeleteInstance(self):
562
    self.rapi.AddResponse("1234")
563
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
564
    self.assertHandler(rlib2.R_2_instances_name)
565
    self.assertItems(["instance"])
566
    self.assertDryRun()
567

    
568
  def testGetInstanceTags(self):
569
    self.rapi.AddResponse("[]")
570
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
571
    self.assertHandler(rlib2.R_2_instances_name_tags)
572
    self.assertItems(["fooinstance"])
573

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

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

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

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

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

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

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

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

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

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

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

    
690
    self.rapi.AddResponse("5175")
691
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
692
    self.assertItems(["instance-moo"])
693
    self.assertQuery("disks", None)
694

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

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

    
710
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
711
    self.assertEqual(data["mode"], "local")
712
    self.assertEqual(data["destination"], "nodeX")
713
    self.assertEqual(data["shutdown"], True)
714

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

    
722
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
723
    self.assertFalse(data)
724

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

    
735
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
736
        self.assertEqual(len(data), 2)
737
        self.assertEqual(data["mode"], mode)
738
        self.assertEqual(data["cleanup"], cleanup)
739

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

    
747
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
748
    self.assertFalse(data)
749

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

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

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

    
778
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
779
    self.assertEqualValues(data, {"new_name": new_name, })
780

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

    
793
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
794
        self.assertEqual(len(data), 3)
795
        self.assertEqual(data["new_name"], new_name)
796
        self.assertEqual(data["ip_check"], ip_check)
797
        self.assertEqual(data["name_check"], name_check)
798

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

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

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

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

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

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

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

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

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

    
863
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
864
    self.rapi.AddResponse("8888")
865
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
866
    self.assertEqual(8888, job_id)
867
    self.assertItems(["node-3"])
868
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
869
                     { "iallocator": "hail", })
870
    self.assertDryRun()
871

    
872
    self.assertRaises(client.GanetiApiError,
873
                      self.client.EvacuateNode,
874
                      "node-4", iallocator="hail", remote_node="node-5")
875
    self.assertEqual(self.rapi.CountPending(), 0)
876

    
877
  def testEvacuateNodeOldResponse(self):
878
    self.rapi.AddResponse(serializer.DumpJson([]))
879
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
880
                      "node-4", accept_old=False)
881
    self.assertEqual(self.rapi.CountPending(), 0)
882

    
883
    self.rapi.AddResponse(serializer.DumpJson([]))
884
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
885
                      "node-4", accept_old=True)
886
    self.assertEqual(self.rapi.CountPending(), 0)
887

    
888
    self.rapi.AddResponse(serializer.DumpJson([]))
889
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
890
                      "node-4", accept_old=True, primary=True)
891
    self.assertEqual(self.rapi.CountPending(), 0)
892

    
893
    self.rapi.AddResponse(serializer.DumpJson([]))
894
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
895
                      "node-4", accept_old=True, secondary=False)
896
    self.assertEqual(self.rapi.CountPending(), 0)
897

    
898
    for sec in [True, None]:
899
      self.rapi.AddResponse(serializer.DumpJson([]))
900
      self.rapi.AddResponse(serializer.DumpJson([["res", "foo"]]))
901
      result = self.client.EvacuateNode("node-3", iallocator="hail",
902
                                        dry_run=True, accept_old=True,
903
                                        primary=False, secondary=sec)
904
      self.assertEqual(result, [["res", "foo"]])
905
      self.assertItems(["node-3"])
906
      self.assertQuery("iallocator", ["hail"])
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)