Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (52.1 kB)

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

    
4
# Copyright (C) 2010, 2011 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
  rlib2.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_NIC_PARAMS, constants.INIC_PARAMS)
153
    self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
154
    self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
155
    self.assertEqual(client.JOB_STATUS_CANCELING,
156
                     constants.JOB_STATUS_CANCELING)
157
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
158
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
159
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
160
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
161
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
162
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
163

    
164
    # Node evacuation
165
    self.assertEqual(client.NODE_EVAC_PRI, constants.NODE_EVAC_PRI)
166
    self.assertEqual(client.NODE_EVAC_SEC, constants.NODE_EVAC_SEC)
167
    self.assertEqual(client.NODE_EVAC_ALL, constants.NODE_EVAC_ALL)
168

    
169
    # Legacy name
170
    self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITING)
171

    
172
    # RAPI feature strings
173
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
174
    self.assertEqual(client.INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
175
    self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
176
    self.assertEqual(client.INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
177
    self.assertEqual(client._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
178
    self.assertEqual(client.NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
179
    self.assertEqual(client._NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
180
    self.assertEqual(client.NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
181

    
182

    
183
class RapiMockTest(unittest.TestCase):
184
  def test(self):
185
    rapi = RapiMock()
186
    path = "/version"
187
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
188
    self.assertEqual((501, "Method not implemented"),
189
                     rapi.FetchResponse("/version", "POST", None))
190
    rapi.AddResponse("2")
191
    code, response = rapi.FetchResponse("/version", "GET", None)
192
    self.assertEqual(200, code)
193
    self.assertEqual("2", response)
194
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
195

    
196

    
197
def _FakeNoSslPycurlVersion():
198
  # Note: incomplete version tuple
199
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
200

    
201

    
202
def _FakeFancySslPycurlVersion():
203
  # Note: incomplete version tuple
204
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
205

    
206

    
207
def _FakeOpenSslPycurlVersion():
208
  # Note: incomplete version tuple
209
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
210

    
211

    
212
def _FakeGnuTlsPycurlVersion():
213
  # Note: incomplete version tuple
214
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
215

    
216

    
217
class TestExtendedConfig(unittest.TestCase):
218
  def testAuth(self):
219
    cl = client.GanetiRapiClient("master.example.com",
220
                                 username="user", password="pw",
221
                                 curl_factory=lambda: FakeCurl(RapiMock()))
222

    
223
    curl = cl._CreateCurl()
224
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
225
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
226

    
227
  def testInvalidAuth(self):
228
    # No username
229
    self.assertRaises(client.Error, client.GanetiRapiClient,
230
                      "master-a.example.com", password="pw")
231
    # No password
232
    self.assertRaises(client.Error, client.GanetiRapiClient,
233
                      "master-b.example.com", username="user")
234

    
235
  def testCertVerifyInvalidCombinations(self):
236
    self.assertRaises(client.Error, client.GenericCurlConfig,
237
                      use_curl_cabundle=True, cafile="cert1.pem")
238
    self.assertRaises(client.Error, client.GenericCurlConfig,
239
                      use_curl_cabundle=True, capath="certs/")
240
    self.assertRaises(client.Error, client.GenericCurlConfig,
241
                      use_curl_cabundle=True,
242
                      cafile="cert1.pem", capath="certs/")
243

    
244
  def testProxySignalVerifyHostname(self):
245
    for use_gnutls in [False, True]:
246
      if use_gnutls:
247
        pcverfn = _FakeGnuTlsPycurlVersion
248
      else:
249
        pcverfn = _FakeOpenSslPycurlVersion
250

    
251
      for proxy in ["", "http://127.0.0.1:1234"]:
252
        for use_signal in [False, True]:
253
          for verify_hostname in [False, True]:
254
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
255
                                             verify_hostname=verify_hostname,
256
                                             _pycurl_version_fn=pcverfn)
257

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

    
263
            curl = cl._CreateCurl()
264
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
265
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
266

    
267
            if verify_hostname:
268
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
269
            else:
270
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
271

    
272
  def testNoCertVerify(self):
273
    cfgfn = client.GenericCurlConfig()
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.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
281
    self.assertFalse(curl.getopt(pycurl.CAINFO))
282
    self.assertFalse(curl.getopt(pycurl.CAPATH))
283

    
284
  def testCertVerifyCurlBundle(self):
285
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
286

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

    
291
    curl = cl._CreateCurl()
292
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
293
    self.assertFalse(curl.getopt(pycurl.CAINFO))
294
    self.assertFalse(curl.getopt(pycurl.CAPATH))
295

    
296
  def testCertVerifyCafile(self):
297
    mycert = "/tmp/some/UNUSED/cert/file.pem"
298
    cfgfn = client.GenericCurlConfig(cafile=mycert)
299

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

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

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

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

    
319
    curl = cl._CreateCurl()
320
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
321
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
322
    self.assertFalse(curl.getopt(pycurl.CAINFO))
323

    
324
  def testCertVerifyCapathGnuTls(self):
325
    certdir = "/tmp/some/UNUSED/cert/directory"
326
    pcverfn = _FakeGnuTlsPycurlVersion
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 testCertVerifyNoSsl(self):
337
    certdir = "/tmp/some/UNUSED/cert/directory"
338
    pcverfn = _FakeNoSslPycurlVersion
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(client.Error, cl._CreateCurl)
347

    
348
  def testCertVerifyFancySsl(self):
349
    certdir = "/tmp/some/UNUSED/cert/directory"
350
    pcverfn = _FakeFancySslPycurlVersion
351
    cfgfn = client.GenericCurlConfig(capath=certdir,
352
                                     _pycurl_version_fn=pcverfn)
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
    self.assertRaises(NotImplementedError, cl._CreateCurl)
359

    
360
  def testCertVerifyCapath(self):
361
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
362
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
363
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
364
                                         timeout=timeout)
365

    
366
        curl_factory = lambda: FakeCurl(RapiMock())
367
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
368
                                     curl_factory=curl_factory)
369

    
370
        curl = cl._CreateCurl()
371
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
372
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
373

    
374

    
375
class GanetiRapiClientTests(testutils.GanetiTestCase):
376
  def setUp(self):
377
    testutils.GanetiTestCase.setUp(self)
378

    
379
    self.rapi = RapiMock()
380
    self.curl = FakeCurl(self.rapi)
381
    self.client = client.GanetiRapiClient("master.example.com",
382
                                          curl_factory=lambda: self.curl)
383

    
384
  def assertHandler(self, handler_cls):
385
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
386

    
387
  def assertQuery(self, key, value):
388
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
389

    
390
  def assertItems(self, items):
391
    self.assertEqual(items, self.rapi.GetLastHandler().items)
392

    
393
  def assertBulk(self):
394
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
395

    
396
  def assertDryRun(self):
397
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
398

    
399
  def assertUseForce(self):
400
    self.assertTrue(self.rapi.GetLastHandler().useForce())
401

    
402
  def testEncodeQuery(self):
403
    query = [
404
      ("a", None),
405
      ("b", 1),
406
      ("c", 2),
407
      ("d", "Foo"),
408
      ("e", True),
409
      ]
410

    
411
    expected = [
412
      ("a", ""),
413
      ("b", 1),
414
      ("c", 2),
415
      ("d", "Foo"),
416
      ("e", 1),
417
      ]
418

    
419
    self.assertEqualValues(self.client._EncodeQuery(query),
420
                           expected)
421

    
422
    # invalid types
423
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
424
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
425

    
426
  def testCurlSettings(self):
427
    self.rapi.AddResponse("2")
428
    self.assertEqual(2, self.client.GetVersion())
429
    self.assertHandler(rlib2.R_version)
430

    
431
    # Signals should be disabled by default
432
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
433

    
434
    # No auth and no proxy
435
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
436
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
437

    
438
    # Content-type is required for requests
439
    headers = self.curl.getopt(pycurl.HTTPHEADER)
440
    self.assert_("Content-type: application/json" in headers)
441

    
442
  def testHttpError(self):
443
    self.rapi.AddResponse(None, code=404)
444
    try:
445
      self.client.GetJobStatus(15140)
446
    except client.GanetiApiError, err:
447
      self.assertEqual(err.code, 404)
448
    else:
449
      self.fail("Didn't raise exception")
450

    
451
  def testGetVersion(self):
452
    self.rapi.AddResponse("2")
453
    self.assertEqual(2, self.client.GetVersion())
454
    self.assertHandler(rlib2.R_version)
455

    
456
  def testGetFeatures(self):
457
    for features in [[], ["foo", "bar", "baz"]]:
458
      self.rapi.AddResponse(serializer.DumpJson(features))
459
      self.assertEqual(features, self.client.GetFeatures())
460
      self.assertHandler(rlib2.R_2_features)
461

    
462
  def testGetFeaturesNotFound(self):
463
    self.rapi.AddResponse(None, code=404)
464
    self.assertEqual([], self.client.GetFeatures())
465

    
466
  def testGetOperatingSystems(self):
467
    self.rapi.AddResponse("[\"beos\"]")
468
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
469
    self.assertHandler(rlib2.R_2_os)
470

    
471
  def testGetClusterTags(self):
472
    self.rapi.AddResponse("[\"tag\"]")
473
    self.assertEqual(["tag"], self.client.GetClusterTags())
474
    self.assertHandler(rlib2.R_2_tags)
475

    
476
  def testAddClusterTags(self):
477
    self.rapi.AddResponse("1234")
478
    self.assertEqual(1234,
479
        self.client.AddClusterTags(["awesome"], dry_run=True))
480
    self.assertHandler(rlib2.R_2_tags)
481
    self.assertDryRun()
482
    self.assertQuery("tag", ["awesome"])
483

    
484
  def testDeleteClusterTags(self):
485
    self.rapi.AddResponse("5107")
486
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
487
                                                         dry_run=True))
488
    self.assertHandler(rlib2.R_2_tags)
489
    self.assertDryRun()
490
    self.assertQuery("tag", ["awesome"])
491

    
492
  def testGetInfo(self):
493
    self.rapi.AddResponse("{}")
494
    self.assertEqual({}, self.client.GetInfo())
495
    self.assertHandler(rlib2.R_2_info)
496

    
497
  def testGetInstances(self):
498
    self.rapi.AddResponse("[]")
499
    self.assertEqual([], self.client.GetInstances(bulk=True))
500
    self.assertHandler(rlib2.R_2_instances)
501
    self.assertBulk()
502

    
503
  def testGetInstance(self):
504
    self.rapi.AddResponse("[]")
505
    self.assertEqual([], self.client.GetInstance("instance"))
506
    self.assertHandler(rlib2.R_2_instances_name)
507
    self.assertItems(["instance"])
508

    
509
  def testGetInstanceInfo(self):
510
    self.rapi.AddResponse("21291")
511
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
512
    self.assertHandler(rlib2.R_2_instances_name_info)
513
    self.assertItems(["inst3"])
514
    self.assertQuery("static", None)
515

    
516
    self.rapi.AddResponse("3428")
517
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
518
    self.assertHandler(rlib2.R_2_instances_name_info)
519
    self.assertItems(["inst31"])
520
    self.assertQuery("static", ["0"])
521

    
522
    self.rapi.AddResponse("15665")
523
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
524
    self.assertHandler(rlib2.R_2_instances_name_info)
525
    self.assertItems(["inst32"])
526
    self.assertQuery("static", ["1"])
527

    
528
  def testCreateInstanceOldVersion(self):
529
    # The old request format, version 0, is no longer supported
530
    self.rapi.AddResponse(None, code=404)
531
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
532
                      "create", "inst1.example.com", "plain", [], [])
533
    self.assertEqual(self.rapi.CountPending(), 0)
534

    
535
  def testCreateInstance(self):
536
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
537
    self.rapi.AddResponse("23030")
538
    job_id = self.client.CreateInstance("create", "inst1.example.com",
539
                                        "plain", [], [], dry_run=True)
540
    self.assertEqual(job_id, 23030)
541
    self.assertHandler(rlib2.R_2_instances)
542
    self.assertDryRun()
543

    
544
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
545

    
546
    for field in ["dry_run", "beparams", "hvparams", "start"]:
547
      self.assertFalse(field in data)
548

    
549
    self.assertEqual(data["name"], "inst1.example.com")
550
    self.assertEqual(data["disk_template"], "plain")
551

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

    
564
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
565
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
566
    self.assertEqual(data["name"], "inst2.example.com")
567
    self.assertEqual(data["disk_template"], "drbd8")
568
    self.assertEqual(data["start"], True)
569
    self.assertEqual(data["ip_check"], False)
570
    self.assertEqualValues(data["disks"], [{"size": 100,}])
571
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
572

    
573
  def testDeleteInstance(self):
574
    self.rapi.AddResponse("1234")
575
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
576
    self.assertHandler(rlib2.R_2_instances_name)
577
    self.assertItems(["instance"])
578
    self.assertDryRun()
579

    
580
  def testGetInstanceTags(self):
581
    self.rapi.AddResponse("[]")
582
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
583
    self.assertHandler(rlib2.R_2_instances_name_tags)
584
    self.assertItems(["fooinstance"])
585

    
586
  def testAddInstanceTags(self):
587
    self.rapi.AddResponse("1234")
588
    self.assertEqual(1234,
589
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
590
    self.assertHandler(rlib2.R_2_instances_name_tags)
591
    self.assertItems(["fooinstance"])
592
    self.assertDryRun()
593
    self.assertQuery("tag", ["awesome"])
594

    
595
  def testDeleteInstanceTags(self):
596
    self.rapi.AddResponse("25826")
597
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
598
                                                           dry_run=True))
599
    self.assertHandler(rlib2.R_2_instances_name_tags)
600
    self.assertItems(["foo"])
601
    self.assertDryRun()
602
    self.assertQuery("tag", ["awesome"])
603

    
604
  def testRebootInstance(self):
605
    self.rapi.AddResponse("6146")
606
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
607
                                        ignore_secondaries=True, dry_run=True)
608
    self.assertEqual(6146, job_id)
609
    self.assertHandler(rlib2.R_2_instances_name_reboot)
610
    self.assertItems(["i-bar"])
611
    self.assertDryRun()
612
    self.assertQuery("type", ["hard"])
613
    self.assertQuery("ignore_secondaries", ["1"])
614

    
615
  def testShutdownInstance(self):
616
    self.rapi.AddResponse("1487")
617
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
618
                                                        dry_run=True))
619
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
620
    self.assertItems(["foo-instance"])
621
    self.assertDryRun()
622

    
623
  def testStartupInstance(self):
624
    self.rapi.AddResponse("27149")
625
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
626
                                                        dry_run=True))
627
    self.assertHandler(rlib2.R_2_instances_name_startup)
628
    self.assertItems(["bar-instance"])
629
    self.assertDryRun()
630

    
631
  def testReinstallInstance(self):
632
    self.rapi.AddResponse(serializer.DumpJson([]))
633
    self.rapi.AddResponse("19119")
634
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
635
                                                          os="DOS",
636
                                                          no_startup=True))
637
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
638
    self.assertItems(["baz-instance"])
639
    self.assertQuery("os", ["DOS"])
640
    self.assertQuery("nostartup", ["1"])
641
    self.assertEqual(self.rapi.CountPending(), 0)
642

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

    
657
  def testReinstallInstanceWithOsparams1(self):
658
    self.rapi.AddResponse(serializer.DumpJson([]))
659
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
660
                      "doo-instance", osparams={"x": "y"})
661
    self.assertEqual(self.rapi.CountPending(), 0)
662

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

    
680
  def testReplaceInstanceDisks(self):
681
    self.rapi.AddResponse("999")
682
    job_id = self.client.ReplaceInstanceDisks("instance-name",
683
        disks=[0, 1], iallocator="hail")
684
    self.assertEqual(999, job_id)
685
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
686
    self.assertItems(["instance-name"])
687
    self.assertQuery("disks", ["0,1"])
688
    self.assertQuery("mode", ["replace_auto"])
689
    self.assertQuery("iallocator", ["hail"])
690

    
691
    self.rapi.AddResponse("1000")
692
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
693
        disks=[1], mode="replace_on_secondary", remote_node="foo-node")
694
    self.assertEqual(1000, job_id)
695
    self.assertItems(["instance-bar"])
696
    self.assertQuery("disks", ["1"])
697
    self.assertQuery("remote_node", ["foo-node"])
698

    
699
    self.rapi.AddResponse("5175")
700
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
701
    self.assertItems(["instance-moo"])
702
    self.assertQuery("disks", None)
703

    
704
  def testPrepareExport(self):
705
    self.rapi.AddResponse("8326")
706
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
707
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
708
    self.assertItems(["inst1"])
709
    self.assertQuery("mode", ["local"])
710

    
711
  def testExportInstance(self):
712
    self.rapi.AddResponse("19695")
713
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
714
                                        shutdown=True)
715
    self.assertEqual(job_id, 19695)
716
    self.assertHandler(rlib2.R_2_instances_name_export)
717
    self.assertItems(["inst2"])
718

    
719
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
720
    self.assertEqual(data["mode"], "local")
721
    self.assertEqual(data["destination"], "nodeX")
722
    self.assertEqual(data["shutdown"], True)
723

    
724
  def testMigrateInstanceDefaults(self):
725
    self.rapi.AddResponse("24873")
726
    job_id = self.client.MigrateInstance("inst91")
727
    self.assertEqual(job_id, 24873)
728
    self.assertHandler(rlib2.R_2_instances_name_migrate)
729
    self.assertItems(["inst91"])
730

    
731
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
732
    self.assertFalse(data)
733

    
734
  def testMigrateInstance(self):
735
    for mode in constants.HT_MIGRATION_MODES:
736
      for cleanup in [False, True]:
737
        self.rapi.AddResponse("31910")
738
        job_id = self.client.MigrateInstance("inst289", mode=mode,
739
                                             cleanup=cleanup)
740
        self.assertEqual(job_id, 31910)
741
        self.assertHandler(rlib2.R_2_instances_name_migrate)
742
        self.assertItems(["inst289"])
743

    
744
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
745
        self.assertEqual(len(data), 2)
746
        self.assertEqual(data["mode"], mode)
747
        self.assertEqual(data["cleanup"], cleanup)
748

    
749
  def testFailoverInstanceDefaults(self):
750
    self.rapi.AddResponse("7639")
751
    job_id = self.client.FailoverInstance("inst13579")
752
    self.assertEqual(job_id, 7639)
753
    self.assertHandler(rlib2.R_2_instances_name_failover)
754
    self.assertItems(["inst13579"])
755

    
756
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
757
    self.assertFalse(data)
758

    
759
  def testFailoverInstance(self):
760
    for iallocator in ["dumb", "hail"]:
761
      for ignore_consistency in [False, True]:
762
        for target_node in ["node-a", "node2"]:
763
          self.rapi.AddResponse("19161")
764
          job_id = \
765
            self.client.FailoverInstance("inst251", iallocator=iallocator,
766
                                         ignore_consistency=ignore_consistency,
767
                                         target_node=target_node)
768
          self.assertEqual(job_id, 19161)
769
          self.assertHandler(rlib2.R_2_instances_name_failover)
770
          self.assertItems(["inst251"])
771

    
772
          data = serializer.LoadJson(self.rapi.GetLastRequestData())
773
          self.assertEqual(len(data), 3)
774
          self.assertEqual(data["iallocator"], iallocator)
775
          self.assertEqual(data["ignore_consistency"], ignore_consistency)
776
          self.assertEqual(data["target_node"], target_node)
777
          self.assertEqual(self.rapi.CountPending(), 0)
778

    
779
  def testRenameInstanceDefaults(self):
780
    new_name = "newnametha7euqu"
781
    self.rapi.AddResponse("8791")
782
    job_id = self.client.RenameInstance("inst18821", new_name)
783
    self.assertEqual(job_id, 8791)
784
    self.assertHandler(rlib2.R_2_instances_name_rename)
785
    self.assertItems(["inst18821"])
786

    
787
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
788
    self.assertEqualValues(data, {"new_name": new_name, })
789

    
790
  def testRenameInstance(self):
791
    new_name = "new-name-yiux1iin"
792
    for ip_check in [False, True]:
793
      for name_check in [False, True]:
794
        self.rapi.AddResponse("24776")
795
        job_id = self.client.RenameInstance("inst20967", new_name,
796
                                             ip_check=ip_check,
797
                                             name_check=name_check)
798
        self.assertEqual(job_id, 24776)
799
        self.assertHandler(rlib2.R_2_instances_name_rename)
800
        self.assertItems(["inst20967"])
801

    
802
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
803
        self.assertEqual(len(data), 3)
804
        self.assertEqual(data["new_name"], new_name)
805
        self.assertEqual(data["ip_check"], ip_check)
806
        self.assertEqual(data["name_check"], name_check)
807

    
808
  def testGetJobs(self):
809
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
810
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
811
    self.assertEqual([123, 124], self.client.GetJobs())
812
    self.assertHandler(rlib2.R_2_jobs)
813

    
814
  def testGetJobStatus(self):
815
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
816
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
817
    self.assertHandler(rlib2.R_2_jobs_id)
818
    self.assertItems(["1234"])
819

    
820
  def testWaitForJobChange(self):
821
    fields = ["id", "summary"]
822
    expected = {
823
      "job_info": [123, "something"],
824
      "log_entries": [],
825
      }
826

    
827
    self.rapi.AddResponse(serializer.DumpJson(expected))
828
    result = self.client.WaitForJobChange(123, fields, [], -1)
829
    self.assertEqualValues(expected, result)
830
    self.assertHandler(rlib2.R_2_jobs_id_wait)
831
    self.assertItems(["123"])
832

    
833
  def testCancelJob(self):
834
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
835
    self.assertEqual([True, "Job 123 will be canceled"],
836
                     self.client.CancelJob(999, dry_run=True))
837
    self.assertHandler(rlib2.R_2_jobs_id)
838
    self.assertItems(["999"])
839
    self.assertDryRun()
840

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

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

    
855
  def testGetNode(self):
856
    self.rapi.AddResponse("{}")
857
    self.assertEqual({}, self.client.GetNode("node-foo"))
858
    self.assertHandler(rlib2.R_2_nodes_name)
859
    self.assertItems(["node-foo"])
860

    
861
  def testEvacuateNode(self):
862
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
863
    self.rapi.AddResponse("9876")
864
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
865
    self.assertEqual(9876, job_id)
866
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
867
    self.assertItems(["node-1"])
868
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
869
                     { "remote_node": "node-2", })
870
    self.assertEqual(self.rapi.CountPending(), 0)
871

    
872
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
873
    self.rapi.AddResponse("8888")
874
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
875
                                      mode=constants.NODE_EVAC_ALL,
876
                                      early_release=True)
877
    self.assertEqual(8888, job_id)
878
    self.assertItems(["node-3"])
879
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
880
      "iallocator": "hail",
881
      "mode": "all",
882
      "early_release": True,
883
      })
884
    self.assertDryRun()
885

    
886
    self.assertRaises(client.GanetiApiError,
887
                      self.client.EvacuateNode,
888
                      "node-4", iallocator="hail", remote_node="node-5")
889
    self.assertEqual(self.rapi.CountPending(), 0)
890

    
891
  def testEvacuateNodeOldResponse(self):
892
    self.rapi.AddResponse(serializer.DumpJson([]))
893
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
894
                      "node-4", accept_old=False)
895
    self.assertEqual(self.rapi.CountPending(), 0)
896

    
897
    for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
898
      self.rapi.AddResponse(serializer.DumpJson([]))
899
      self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
900
                        "node-4", accept_old=True, mode=mode)
901
      self.assertEqual(self.rapi.CountPending(), 0)
902

    
903
    self.rapi.AddResponse(serializer.DumpJson([]))
904
    self.rapi.AddResponse(serializer.DumpJson("21533"))
905
    result = self.client.EvacuateNode("node-3", iallocator="hail",
906
                                      dry_run=True, accept_old=True,
907
                                      mode=client.NODE_EVAC_SEC,
908
                                      early_release=True)
909
    self.assertEqual(result, "21533")
910
    self.assertItems(["node-3"])
911
    self.assertQuery("iallocator", ["hail"])
912
    self.assertQuery("early_release", ["1"])
913
    self.assertFalse(self.rapi.GetLastRequestData())
914
    self.assertDryRun()
915
    self.assertEqual(self.rapi.CountPending(), 0)
916

    
917
  def testMigrateNode(self):
918
    self.rapi.AddResponse(serializer.DumpJson([]))
919
    self.rapi.AddResponse("1111")
920
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
921
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
922
    self.assertItems(["node-a"])
923
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
924
    self.assertDryRun()
925
    self.assertFalse(self.rapi.GetLastRequestData())
926

    
927
    self.rapi.AddResponse(serializer.DumpJson([]))
928
    self.rapi.AddResponse("1112")
929
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
930
                                                   mode="live"))
931
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
932
    self.assertItems(["node-a"])
933
    self.assertQuery("mode", ["live"])
934
    self.assertDryRun()
935
    self.assertFalse(self.rapi.GetLastRequestData())
936

    
937
    self.rapi.AddResponse(serializer.DumpJson([]))
938
    self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
939
                      "node-c", target_node="foonode")
940
    self.assertEqual(self.rapi.CountPending(), 0)
941

    
942
  def testMigrateNodeBodyData(self):
943
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
944
    self.rapi.AddResponse("27539")
945
    self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
946
                                                    mode="live"))
947
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
948
    self.assertItems(["node-a"])
949
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
950
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
951
                     { "mode": "live", })
952

    
953
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
954
    self.rapi.AddResponse("14219")
955
    self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
956
                                                    target_node="node9",
957
                                                    iallocator="ial"))
958
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
959
    self.assertItems(["node-x"])
960
    self.assertDryRun()
961
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
962
                     { "target_node": "node9", "iallocator": "ial", })
963

    
964
    self.assertEqual(self.rapi.CountPending(), 0)
965

    
966
  def testGetNodeRole(self):
967
    self.rapi.AddResponse("\"master\"")
968
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
969
    self.assertHandler(rlib2.R_2_nodes_name_role)
970
    self.assertItems(["node-a"])
971

    
972
  def testSetNodeRole(self):
973
    self.rapi.AddResponse("789")
974
    self.assertEqual(789,
975
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
976
    self.assertHandler(rlib2.R_2_nodes_name_role)
977
    self.assertItems(["node-foo"])
978
    self.assertQuery("force", ["1"])
979
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
980

    
981
  def testPowercycleNode(self):
982
    self.rapi.AddResponse("23051")
983
    self.assertEqual(23051,
984
        self.client.PowercycleNode("node5468", force=True))
985
    self.assertHandler(rlib2.R_2_nodes_name_powercycle)
986
    self.assertItems(["node5468"])
987
    self.assertQuery("force", ["1"])
988
    self.assertFalse(self.rapi.GetLastRequestData())
989
    self.assertEqual(self.rapi.CountPending(), 0)
990

    
991
  def testModifyNode(self):
992
    self.rapi.AddResponse("3783")
993
    job_id = self.client.ModifyNode("node16979.example.com", drained=True)
994
    self.assertEqual(job_id, 3783)
995
    self.assertHandler(rlib2.R_2_nodes_name_modify)
996
    self.assertItems(["node16979.example.com"])
997
    self.assertEqual(self.rapi.CountPending(), 0)
998

    
999
  def testGetNodeStorageUnits(self):
1000
    self.rapi.AddResponse("42")
1001
    self.assertEqual(42,
1002
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
1003
    self.assertHandler(rlib2.R_2_nodes_name_storage)
1004
    self.assertItems(["node-x"])
1005
    self.assertQuery("storage_type", ["lvm-pv"])
1006
    self.assertQuery("output_fields", ["fields"])
1007

    
1008
  def testModifyNodeStorageUnits(self):
1009
    self.rapi.AddResponse("14")
1010
    self.assertEqual(14,
1011
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
1012
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1013
    self.assertItems(["node-z"])
1014
    self.assertQuery("storage_type", ["lvm-pv"])
1015
    self.assertQuery("name", ["hda"])
1016
    self.assertQuery("allocatable", None)
1017

    
1018
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
1019
      self.rapi.AddResponse("7205")
1020
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
1021
                                                  allocatable=allocatable)
1022
      self.assertEqual(7205, job_id)
1023
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1024
      self.assertItems(["node-z"])
1025
      self.assertQuery("storage_type", ["lvm-pv"])
1026
      self.assertQuery("name", ["hda"])
1027
      self.assertQuery("allocatable", [query_allocatable])
1028

    
1029
  def testRepairNodeStorageUnits(self):
1030
    self.rapi.AddResponse("99")
1031
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1032
                                                            "hda"))
1033
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1034
    self.assertItems(["node-z"])
1035
    self.assertQuery("storage_type", ["lvm-pv"])
1036
    self.assertQuery("name", ["hda"])
1037

    
1038
  def testGetNodeTags(self):
1039
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
1040
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1041
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1042
    self.assertItems(["node-k"])
1043

    
1044
  def testAddNodeTags(self):
1045
    self.rapi.AddResponse("1234")
1046
    self.assertEqual(1234,
1047
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1048
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1049
    self.assertItems(["node-v"])
1050
    self.assertDryRun()
1051
    self.assertQuery("tag", ["awesome"])
1052

    
1053
  def testDeleteNodeTags(self):
1054
    self.rapi.AddResponse("16861")
1055
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1056
                                                       dry_run=True))
1057
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1058
    self.assertItems(["node-w"])
1059
    self.assertDryRun()
1060
    self.assertQuery("tag", ["awesome"])
1061

    
1062
  def testGetGroups(self):
1063
    groups = [{"name": "group1",
1064
               "uri": "/2/groups/group1",
1065
               },
1066
              {"name": "group2",
1067
               "uri": "/2/groups/group2",
1068
               },
1069
              ]
1070
    self.rapi.AddResponse(serializer.DumpJson(groups))
1071
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
1072
    self.assertHandler(rlib2.R_2_groups)
1073

    
1074
  def testGetGroupsBulk(self):
1075
    groups = [{"name": "group1",
1076
               "uri": "/2/groups/group1",
1077
               "node_cnt": 2,
1078
               "node_list": ["gnt1.test",
1079
                             "gnt2.test",
1080
                             ],
1081
               },
1082
              {"name": "group2",
1083
               "uri": "/2/groups/group2",
1084
               "node_cnt": 1,
1085
               "node_list": ["gnt3.test",
1086
                             ],
1087
               },
1088
              ]
1089
    self.rapi.AddResponse(serializer.DumpJson(groups))
1090

    
1091
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
1092
    self.assertHandler(rlib2.R_2_groups)
1093
    self.assertBulk()
1094

    
1095
  def testGetGroup(self):
1096
    group = {"ctime": None,
1097
             "name": "default",
1098
             }
1099
    self.rapi.AddResponse(serializer.DumpJson(group))
1100
    self.assertEqual({"ctime": None, "name": "default"},
1101
                     self.client.GetGroup("default"))
1102
    self.assertHandler(rlib2.R_2_groups_name)
1103
    self.assertItems(["default"])
1104

    
1105
  def testCreateGroup(self):
1106
    self.rapi.AddResponse("12345")
1107
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
1108
    self.assertEqual(job_id, 12345)
1109
    self.assertHandler(rlib2.R_2_groups)
1110
    self.assertDryRun()
1111

    
1112
  def testDeleteGroup(self):
1113
    self.rapi.AddResponse("12346")
1114
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1115
    self.assertEqual(job_id, 12346)
1116
    self.assertHandler(rlib2.R_2_groups_name)
1117
    self.assertDryRun()
1118

    
1119
  def testRenameGroup(self):
1120
    self.rapi.AddResponse("12347")
1121
    job_id = self.client.RenameGroup("oldname", "newname")
1122
    self.assertEqual(job_id, 12347)
1123
    self.assertHandler(rlib2.R_2_groups_name_rename)
1124

    
1125
  def testModifyGroup(self):
1126
    self.rapi.AddResponse("12348")
1127
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1128
    self.assertEqual(job_id, 12348)
1129
    self.assertHandler(rlib2.R_2_groups_name_modify)
1130

    
1131
  def testAssignGroupNodes(self):
1132
    self.rapi.AddResponse("12349")
1133
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1134
                                          force=True, dry_run=True)
1135
    self.assertEqual(job_id, 12349)
1136
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1137
    self.assertDryRun()
1138
    self.assertUseForce()
1139

    
1140
  def testModifyInstance(self):
1141
    self.rapi.AddResponse("23681")
1142
    job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1143
    self.assertEqual(job_id, 23681)
1144
    self.assertItems(["inst7210"])
1145
    self.assertHandler(rlib2.R_2_instances_name_modify)
1146
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1147
                     { "os_name": "linux", })
1148

    
1149
  def testModifyCluster(self):
1150
    for mnh in [None, False, True]:
1151
      self.rapi.AddResponse("14470")
1152
      self.assertEqual(14470,
1153
        self.client.ModifyCluster(maintain_node_health=mnh))
1154
      self.assertHandler(rlib2.R_2_cluster_modify)
1155
      self.assertItems([])
1156
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1157
      self.assertEqual(len(data), 1)
1158
      self.assertEqual(data["maintain_node_health"], mnh)
1159
      self.assertEqual(self.rapi.CountPending(), 0)
1160

    
1161
  def testRedistributeConfig(self):
1162
    self.rapi.AddResponse("3364")
1163
    job_id = self.client.RedistributeConfig()
1164
    self.assertEqual(job_id, 3364)
1165
    self.assertItems([])
1166
    self.assertHandler(rlib2.R_2_redist_config)
1167

    
1168
  def testActivateInstanceDisks(self):
1169
    self.rapi.AddResponse("23547")
1170
    job_id = self.client.ActivateInstanceDisks("inst28204")
1171
    self.assertEqual(job_id, 23547)
1172
    self.assertItems(["inst28204"])
1173
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1174
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1175

    
1176
  def testActivateInstanceDisksIgnoreSize(self):
1177
    self.rapi.AddResponse("11044")
1178
    job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1179
    self.assertEqual(job_id, 11044)
1180
    self.assertItems(["inst28204"])
1181
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1182
    self.assertQuery("ignore_size", ["1"])
1183

    
1184
  def testDeactivateInstanceDisks(self):
1185
    self.rapi.AddResponse("14591")
1186
    job_id = self.client.DeactivateInstanceDisks("inst28234")
1187
    self.assertEqual(job_id, 14591)
1188
    self.assertItems(["inst28234"])
1189
    self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1190
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1191

    
1192
  def testRecreateInstanceDisks(self):
1193
    self.rapi.AddResponse("13553")
1194
    job_id = self.client.RecreateInstanceDisks("inst23153")
1195
    self.assertEqual(job_id, 13553)
1196
    self.assertItems(["inst23153"])
1197
    self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1198
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1199

    
1200
  def testGetInstanceConsole(self):
1201
    self.rapi.AddResponse("26876")
1202
    job_id = self.client.GetInstanceConsole("inst21491")
1203
    self.assertEqual(job_id, 26876)
1204
    self.assertItems(["inst21491"])
1205
    self.assertHandler(rlib2.R_2_instances_name_console)
1206
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1207
    self.assertFalse(self.rapi.GetLastRequestData())
1208

    
1209
  def testGrowInstanceDisk(self):
1210
    for idx, wait_for_sync in enumerate([None, False, True]):
1211
      amount = 128 + (512 * idx)
1212
      self.assertEqual(self.rapi.CountPending(), 0)
1213
      self.rapi.AddResponse("30783")
1214
      self.assertEqual(30783,
1215
        self.client.GrowInstanceDisk("eze8ch", idx, amount,
1216
                                     wait_for_sync=wait_for_sync))
1217
      self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1218
      self.assertItems(["eze8ch", str(idx)])
1219
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1220
      if wait_for_sync is None:
1221
        self.assertEqual(len(data), 1)
1222
        self.assert_("wait_for_sync" not in data)
1223
      else:
1224
        self.assertEqual(len(data), 2)
1225
        self.assertEqual(data["wait_for_sync"], wait_for_sync)
1226
      self.assertEqual(data["amount"], amount)
1227
      self.assertEqual(self.rapi.CountPending(), 0)
1228

    
1229
  def testGetGroupTags(self):
1230
    self.rapi.AddResponse("[]")
1231
    self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1232
    self.assertHandler(rlib2.R_2_groups_name_tags)
1233
    self.assertItems(["fooGroup"])
1234

    
1235
  def testAddGroupTags(self):
1236
    self.rapi.AddResponse("1234")
1237
    self.assertEqual(1234,
1238
        self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1239
    self.assertHandler(rlib2.R_2_groups_name_tags)
1240
    self.assertItems(["fooGroup"])
1241
    self.assertDryRun()
1242
    self.assertQuery("tag", ["awesome"])
1243

    
1244
  def testDeleteGroupTags(self):
1245
    self.rapi.AddResponse("25826")
1246
    self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1247
                                                        dry_run=True))
1248
    self.assertHandler(rlib2.R_2_groups_name_tags)
1249
    self.assertItems(["foo"])
1250
    self.assertDryRun()
1251
    self.assertQuery("tag", ["awesome"])
1252

    
1253
  def testQuery(self):
1254
    for idx, what in enumerate(constants.QR_VIA_RAPI):
1255
      for idx2, qfilter in enumerate([None, ["?", "name"]]):
1256
        job_id = 11010 + (idx << 4) + (idx2 << 16)
1257
        fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1258

    
1259
        self.rapi.AddResponse(str(job_id))
1260
        self.assertEqual(self.client.Query(what, fields, qfilter=qfilter),
1261
                         job_id)
1262
        self.assertItems([what])
1263
        self.assertHandler(rlib2.R_2_query)
1264
        self.assertFalse(self.rapi.GetLastHandler().queryargs)
1265
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
1266
        self.assertEqual(data["fields"], fields)
1267
        if qfilter is None:
1268
          self.assertTrue("qfilter" not in data)
1269
        else:
1270
          self.assertEqual(data["qfilter"], qfilter)
1271
        self.assertEqual(self.rapi.CountPending(), 0)
1272

    
1273
  def testQueryFields(self):
1274
    exp_result = objects.QueryFieldsResponse(fields=[
1275
      objects.QueryFieldDefinition(name="pnode", title="PNode",
1276
                                   kind=constants.QFT_NUMBER),
1277
      objects.QueryFieldDefinition(name="other", title="Other",
1278
                                   kind=constants.QFT_BOOL),
1279
      ])
1280

    
1281
    for what in constants.QR_VIA_RAPI:
1282
      for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1283
        self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1284
        result = self.client.QueryFields(what, fields=fields)
1285
        self.assertItems([what])
1286
        self.assertHandler(rlib2.R_2_query_fields)
1287
        self.assertFalse(self.rapi.GetLastRequestData())
1288

    
1289
        queryargs = self.rapi.GetLastHandler().queryargs
1290
        if fields is None:
1291
          self.assertFalse(queryargs)
1292
        else:
1293
          self.assertEqual(queryargs, {
1294
            "fields": [",".join(fields)],
1295
            })
1296

    
1297
        self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1298
                         exp_result.ToDict())
1299

    
1300
        self.assertEqual(self.rapi.CountPending(), 0)
1301

    
1302
  def testWaitForJobCompletionNoChange(self):
1303
    resp = serializer.DumpJson({
1304
      "status": constants.JOB_STATUS_WAITING,
1305
      })
1306

    
1307
    for retries in [1, 5, 25]:
1308
      for _ in range(retries):
1309
        self.rapi.AddResponse(resp)
1310

    
1311
      self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1312
                                                        retries=retries))
1313
      self.assertHandler(rlib2.R_2_jobs_id)
1314
      self.assertItems(["22789"])
1315

    
1316
      self.assertEqual(self.rapi.CountPending(), 0)
1317

    
1318
  def testWaitForJobCompletionAlreadyFinished(self):
1319
    self.rapi.AddResponse(serializer.DumpJson({
1320
      "status": constants.JOB_STATUS_SUCCESS,
1321
      }))
1322

    
1323
    self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1324
                                                     retries=1))
1325
    self.assertHandler(rlib2.R_2_jobs_id)
1326
    self.assertItems(["22793"])
1327

    
1328
    self.assertEqual(self.rapi.CountPending(), 0)
1329

    
1330
  def testWaitForJobCompletionEmptyResponse(self):
1331
    self.rapi.AddResponse("{}")
1332
    self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1333
                                                     retries=10))
1334
    self.assertHandler(rlib2.R_2_jobs_id)
1335
    self.assertItems(["22793"])
1336

    
1337
    self.assertEqual(self.rapi.CountPending(), 0)
1338

    
1339
  def testWaitForJobCompletionOutOfRetries(self):
1340
    for retries in [3, 10, 21]:
1341
      for _ in range(retries):
1342
        self.rapi.AddResponse(serializer.DumpJson({
1343
          "status": constants.JOB_STATUS_RUNNING,
1344
          }))
1345

    
1346
      self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1347
                                                        retries=retries - 1))
1348
      self.assertHandler(rlib2.R_2_jobs_id)
1349
      self.assertItems(["30948"])
1350

    
1351
      self.assertEqual(self.rapi.CountPending(), 1)
1352
      self.rapi.ResetResponses()
1353

    
1354
  def testWaitForJobCompletionSuccessAndFailure(self):
1355
    for retries in [1, 4, 13]:
1356
      for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1357
                                    (True, constants.JOB_STATUS_SUCCESS)]:
1358
        for _ in range(retries):
1359
          self.rapi.AddResponse(serializer.DumpJson({
1360
            "status": constants.JOB_STATUS_RUNNING,
1361
            }))
1362

    
1363
        self.rapi.AddResponse(serializer.DumpJson({
1364
          "status": end_status,
1365
          }))
1366

    
1367
        result = self.client.WaitForJobCompletion(3187, period=None,
1368
                                                  retries=retries + 1)
1369
        self.assertEqual(result, success)
1370
        self.assertHandler(rlib2.R_2_jobs_id)
1371
        self.assertItems(["3187"])
1372

    
1373
        self.assertEqual(self.rapi.CountPending(), 0)
1374

    
1375

    
1376
class RapiTestRunner(unittest.TextTestRunner):
1377
  def run(self, *args):
1378
    global _used_handlers
1379
    assert _used_handlers is None
1380

    
1381
    _used_handlers = set()
1382
    try:
1383
      # Run actual tests
1384
      result = unittest.TextTestRunner.run(self, *args)
1385

    
1386
      diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1387
             _KNOWN_UNUSED)
1388
      if diff:
1389
        raise AssertionError("The following RAPI resources were not used by the"
1390
                             " RAPI client: %r" % utils.CommaJoin(diff))
1391
    finally:
1392
      # Reset global variable
1393
      _used_handlers = None
1394

    
1395
    return result
1396

    
1397

    
1398
if __name__ == '__main__':
1399
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)