Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ cb92e7a1

History | View | Annotate | Download (51.1 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.NODE_EVAC_PRI)
170
    self.assertEqual(client.NODE_EVAC_SEC, constants.NODE_EVAC_SEC)
171
    self.assertEqual(client.NODE_EVAC_ALL, constants.NODE_EVAC_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.NODE_EVAC_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 testModifyNode(self):
976
    self.rapi.AddResponse("3783")
977
    job_id = self.client.ModifyNode("node16979.example.com", drained=True)
978
    self.assertEqual(job_id, 3783)
979
    self.assertHandler(rlib2.R_2_nodes_name_modify)
980
    self.assertItems(["node16979.example.com"])
981
    self.assertEqual(self.rapi.CountPending(), 0)
982

    
983
  def testGetNodeStorageUnits(self):
984
    self.rapi.AddResponse("42")
985
    self.assertEqual(42,
986
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
987
    self.assertHandler(rlib2.R_2_nodes_name_storage)
988
    self.assertItems(["node-x"])
989
    self.assertQuery("storage_type", ["lvm-pv"])
990
    self.assertQuery("output_fields", ["fields"])
991

    
992
  def testModifyNodeStorageUnits(self):
993
    self.rapi.AddResponse("14")
994
    self.assertEqual(14,
995
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
996
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
997
    self.assertItems(["node-z"])
998
    self.assertQuery("storage_type", ["lvm-pv"])
999
    self.assertQuery("name", ["hda"])
1000
    self.assertQuery("allocatable", None)
1001

    
1002
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
1003
      self.rapi.AddResponse("7205")
1004
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
1005
                                                  allocatable=allocatable)
1006
      self.assertEqual(7205, job_id)
1007
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1008
      self.assertItems(["node-z"])
1009
      self.assertQuery("storage_type", ["lvm-pv"])
1010
      self.assertQuery("name", ["hda"])
1011
      self.assertQuery("allocatable", [query_allocatable])
1012

    
1013
  def testRepairNodeStorageUnits(self):
1014
    self.rapi.AddResponse("99")
1015
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1016
                                                            "hda"))
1017
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1018
    self.assertItems(["node-z"])
1019
    self.assertQuery("storage_type", ["lvm-pv"])
1020
    self.assertQuery("name", ["hda"])
1021

    
1022
  def testGetNodeTags(self):
1023
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
1024
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1025
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1026
    self.assertItems(["node-k"])
1027

    
1028
  def testAddNodeTags(self):
1029
    self.rapi.AddResponse("1234")
1030
    self.assertEqual(1234,
1031
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1032
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1033
    self.assertItems(["node-v"])
1034
    self.assertDryRun()
1035
    self.assertQuery("tag", ["awesome"])
1036

    
1037
  def testDeleteNodeTags(self):
1038
    self.rapi.AddResponse("16861")
1039
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1040
                                                       dry_run=True))
1041
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1042
    self.assertItems(["node-w"])
1043
    self.assertDryRun()
1044
    self.assertQuery("tag", ["awesome"])
1045

    
1046
  def testGetGroups(self):
1047
    groups = [{"name": "group1",
1048
               "uri": "/2/groups/group1",
1049
               },
1050
              {"name": "group2",
1051
               "uri": "/2/groups/group2",
1052
               },
1053
              ]
1054
    self.rapi.AddResponse(serializer.DumpJson(groups))
1055
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
1056
    self.assertHandler(rlib2.R_2_groups)
1057

    
1058
  def testGetGroupsBulk(self):
1059
    groups = [{"name": "group1",
1060
               "uri": "/2/groups/group1",
1061
               "node_cnt": 2,
1062
               "node_list": ["gnt1.test",
1063
                             "gnt2.test",
1064
                             ],
1065
               },
1066
              {"name": "group2",
1067
               "uri": "/2/groups/group2",
1068
               "node_cnt": 1,
1069
               "node_list": ["gnt3.test",
1070
                             ],
1071
               },
1072
              ]
1073
    self.rapi.AddResponse(serializer.DumpJson(groups))
1074

    
1075
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
1076
    self.assertHandler(rlib2.R_2_groups)
1077
    self.assertBulk()
1078

    
1079
  def testGetGroup(self):
1080
    group = {"ctime": None,
1081
             "name": "default",
1082
             }
1083
    self.rapi.AddResponse(serializer.DumpJson(group))
1084
    self.assertEqual({"ctime": None, "name": "default"},
1085
                     self.client.GetGroup("default"))
1086
    self.assertHandler(rlib2.R_2_groups_name)
1087
    self.assertItems(["default"])
1088

    
1089
  def testCreateGroup(self):
1090
    self.rapi.AddResponse("12345")
1091
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
1092
    self.assertEqual(job_id, 12345)
1093
    self.assertHandler(rlib2.R_2_groups)
1094
    self.assertDryRun()
1095

    
1096
  def testDeleteGroup(self):
1097
    self.rapi.AddResponse("12346")
1098
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1099
    self.assertEqual(job_id, 12346)
1100
    self.assertHandler(rlib2.R_2_groups_name)
1101
    self.assertDryRun()
1102

    
1103
  def testRenameGroup(self):
1104
    self.rapi.AddResponse("12347")
1105
    job_id = self.client.RenameGroup("oldname", "newname")
1106
    self.assertEqual(job_id, 12347)
1107
    self.assertHandler(rlib2.R_2_groups_name_rename)
1108

    
1109
  def testModifyGroup(self):
1110
    self.rapi.AddResponse("12348")
1111
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1112
    self.assertEqual(job_id, 12348)
1113
    self.assertHandler(rlib2.R_2_groups_name_modify)
1114

    
1115
  def testAssignGroupNodes(self):
1116
    self.rapi.AddResponse("12349")
1117
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1118
                                          force=True, dry_run=True)
1119
    self.assertEqual(job_id, 12349)
1120
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1121
    self.assertDryRun()
1122
    self.assertUseForce()
1123

    
1124
  def testModifyInstance(self):
1125
    self.rapi.AddResponse("23681")
1126
    job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1127
    self.assertEqual(job_id, 23681)
1128
    self.assertItems(["inst7210"])
1129
    self.assertHandler(rlib2.R_2_instances_name_modify)
1130
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1131
                     { "os_name": "linux", })
1132

    
1133
  def testModifyCluster(self):
1134
    for mnh in [None, False, True]:
1135
      self.rapi.AddResponse("14470")
1136
      self.assertEqual(14470,
1137
        self.client.ModifyCluster(maintain_node_health=mnh))
1138
      self.assertHandler(rlib2.R_2_cluster_modify)
1139
      self.assertItems([])
1140
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1141
      self.assertEqual(len(data), 1)
1142
      self.assertEqual(data["maintain_node_health"], mnh)
1143
      self.assertEqual(self.rapi.CountPending(), 0)
1144

    
1145
  def testRedistributeConfig(self):
1146
    self.rapi.AddResponse("3364")
1147
    job_id = self.client.RedistributeConfig()
1148
    self.assertEqual(job_id, 3364)
1149
    self.assertItems([])
1150
    self.assertHandler(rlib2.R_2_redist_config)
1151

    
1152
  def testActivateInstanceDisks(self):
1153
    self.rapi.AddResponse("23547")
1154
    job_id = self.client.ActivateInstanceDisks("inst28204")
1155
    self.assertEqual(job_id, 23547)
1156
    self.assertItems(["inst28204"])
1157
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1158
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1159

    
1160
  def testActivateInstanceDisksIgnoreSize(self):
1161
    self.rapi.AddResponse("11044")
1162
    job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1163
    self.assertEqual(job_id, 11044)
1164
    self.assertItems(["inst28204"])
1165
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1166
    self.assertQuery("ignore_size", ["1"])
1167

    
1168
  def testDeactivateInstanceDisks(self):
1169
    self.rapi.AddResponse("14591")
1170
    job_id = self.client.DeactivateInstanceDisks("inst28234")
1171
    self.assertEqual(job_id, 14591)
1172
    self.assertItems(["inst28234"])
1173
    self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1174
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1175

    
1176
  def testGetInstanceConsole(self):
1177
    self.rapi.AddResponse("26876")
1178
    job_id = self.client.GetInstanceConsole("inst21491")
1179
    self.assertEqual(job_id, 26876)
1180
    self.assertItems(["inst21491"])
1181
    self.assertHandler(rlib2.R_2_instances_name_console)
1182
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1183
    self.assertFalse(self.rapi.GetLastRequestData())
1184

    
1185
  def testGrowInstanceDisk(self):
1186
    for idx, wait_for_sync in enumerate([None, False, True]):
1187
      amount = 128 + (512 * idx)
1188
      self.assertEqual(self.rapi.CountPending(), 0)
1189
      self.rapi.AddResponse("30783")
1190
      self.assertEqual(30783,
1191
        self.client.GrowInstanceDisk("eze8ch", idx, amount,
1192
                                     wait_for_sync=wait_for_sync))
1193
      self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1194
      self.assertItems(["eze8ch", str(idx)])
1195
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1196
      if wait_for_sync is None:
1197
        self.assertEqual(len(data), 1)
1198
        self.assert_("wait_for_sync" not in data)
1199
      else:
1200
        self.assertEqual(len(data), 2)
1201
        self.assertEqual(data["wait_for_sync"], wait_for_sync)
1202
      self.assertEqual(data["amount"], amount)
1203
      self.assertEqual(self.rapi.CountPending(), 0)
1204

    
1205
  def testGetGroupTags(self):
1206
    self.rapi.AddResponse("[]")
1207
    self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1208
    self.assertHandler(rlib2.R_2_groups_name_tags)
1209
    self.assertItems(["fooGroup"])
1210

    
1211
  def testAddGroupTags(self):
1212
    self.rapi.AddResponse("1234")
1213
    self.assertEqual(1234,
1214
        self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1215
    self.assertHandler(rlib2.R_2_groups_name_tags)
1216
    self.assertItems(["fooGroup"])
1217
    self.assertDryRun()
1218
    self.assertQuery("tag", ["awesome"])
1219

    
1220
  def testDeleteGroupTags(self):
1221
    self.rapi.AddResponse("25826")
1222
    self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1223
                                                        dry_run=True))
1224
    self.assertHandler(rlib2.R_2_groups_name_tags)
1225
    self.assertItems(["foo"])
1226
    self.assertDryRun()
1227
    self.assertQuery("tag", ["awesome"])
1228

    
1229
  def testQuery(self):
1230
    for idx, what in enumerate(constants.QR_VIA_RAPI):
1231
      for idx2, filter_ in enumerate([None, ["?", "name"]]):
1232
        job_id = 11010 + (idx << 4) + (idx2 << 16)
1233
        fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1234

    
1235
        self.rapi.AddResponse(str(job_id))
1236
        self.assertEqual(self.client.Query(what, fields, filter_=filter_),
1237
                         job_id)
1238
        self.assertItems([what])
1239
        self.assertHandler(rlib2.R_2_query)
1240
        self.assertFalse(self.rapi.GetLastHandler().queryargs)
1241
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
1242
        self.assertEqual(data["fields"], fields)
1243
        if filter_ is None:
1244
          self.assertTrue("filter" not in data)
1245
        else:
1246
          self.assertEqual(data["filter"], filter_)
1247
        self.assertEqual(self.rapi.CountPending(), 0)
1248

    
1249
  def testQueryFields(self):
1250
    exp_result = objects.QueryFieldsResponse(fields=[
1251
      objects.QueryFieldDefinition(name="pnode", title="PNode",
1252
                                   kind=constants.QFT_NUMBER),
1253
      objects.QueryFieldDefinition(name="other", title="Other",
1254
                                   kind=constants.QFT_BOOL),
1255
      ])
1256

    
1257
    for what in constants.QR_VIA_RAPI:
1258
      for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1259
        self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1260
        result = self.client.QueryFields(what, fields=fields)
1261
        self.assertItems([what])
1262
        self.assertHandler(rlib2.R_2_query_fields)
1263
        self.assertFalse(self.rapi.GetLastRequestData())
1264

    
1265
        queryargs = self.rapi.GetLastHandler().queryargs
1266
        if fields is None:
1267
          self.assertFalse(queryargs)
1268
        else:
1269
          self.assertEqual(queryargs, {
1270
            "fields": [",".join(fields)],
1271
            })
1272

    
1273
        self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1274
                         exp_result.ToDict())
1275

    
1276
        self.assertEqual(self.rapi.CountPending(), 0)
1277

    
1278
  def testWaitForJobCompletionNoChange(self):
1279
    resp = serializer.DumpJson({
1280
      "status": constants.JOB_STATUS_WAITING,
1281
      })
1282

    
1283
    for retries in [1, 5, 25]:
1284
      for _ in range(retries):
1285
        self.rapi.AddResponse(resp)
1286

    
1287
      self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1288
                                                        retries=retries))
1289
      self.assertHandler(rlib2.R_2_jobs_id)
1290
      self.assertItems(["22789"])
1291

    
1292
      self.assertEqual(self.rapi.CountPending(), 0)
1293

    
1294
  def testWaitForJobCompletionAlreadyFinished(self):
1295
    self.rapi.AddResponse(serializer.DumpJson({
1296
      "status": constants.JOB_STATUS_SUCCESS,
1297
      }))
1298

    
1299
    self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1300
                                                     retries=1))
1301
    self.assertHandler(rlib2.R_2_jobs_id)
1302
    self.assertItems(["22793"])
1303

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

    
1306
  def testWaitForJobCompletionEmptyResponse(self):
1307
    self.rapi.AddResponse("{}")
1308
    self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1309
                                                     retries=10))
1310
    self.assertHandler(rlib2.R_2_jobs_id)
1311
    self.assertItems(["22793"])
1312

    
1313
    self.assertEqual(self.rapi.CountPending(), 0)
1314

    
1315
  def testWaitForJobCompletionOutOfRetries(self):
1316
    for retries in [3, 10, 21]:
1317
      for _ in range(retries):
1318
        self.rapi.AddResponse(serializer.DumpJson({
1319
          "status": constants.JOB_STATUS_RUNNING,
1320
          }))
1321

    
1322
      self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1323
                                                        retries=retries - 1))
1324
      self.assertHandler(rlib2.R_2_jobs_id)
1325
      self.assertItems(["30948"])
1326

    
1327
      self.assertEqual(self.rapi.CountPending(), 1)
1328
      self.rapi.ResetResponses()
1329

    
1330
  def testWaitForJobCompletionSuccessAndFailure(self):
1331
    for retries in [1, 4, 13]:
1332
      for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1333
                                    (True, constants.JOB_STATUS_SUCCESS)]:
1334
        for _ in range(retries):
1335
          self.rapi.AddResponse(serializer.DumpJson({
1336
            "status": constants.JOB_STATUS_RUNNING,
1337
            }))
1338

    
1339
        self.rapi.AddResponse(serializer.DumpJson({
1340
          "status": end_status,
1341
          }))
1342

    
1343
        result = self.client.WaitForJobCompletion(3187, period=None,
1344
                                                  retries=retries + 1)
1345
        self.assertEqual(result, success)
1346
        self.assertHandler(rlib2.R_2_jobs_id)
1347
        self.assertItems(["3187"])
1348

    
1349
        self.assertEqual(self.rapi.CountPending(), 0)
1350

    
1351

    
1352
class RapiTestRunner(unittest.TextTestRunner):
1353
  def run(self, *args):
1354
    global _used_handlers
1355
    assert _used_handlers is None
1356

    
1357
    _used_handlers = set()
1358
    try:
1359
      # Run actual tests
1360
      result = unittest.TextTestRunner.run(self, *args)
1361

    
1362
      diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1363
             _KNOWN_UNUSED)
1364
      if diff:
1365
        raise AssertionError("The following RAPI resources were not used by the"
1366
                             " RAPI client: %r" % utils.CommaJoin(diff))
1367
    finally:
1368
      # Reset global variable
1369
      _used_handlers = None
1370

    
1371
    return result
1372

    
1373

    
1374
if __name__ == '__main__':
1375
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)