Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 415feb2e

History | View | Annotate | Download (52 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.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
153
    self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
154
    self.assertEqual(client.JOB_STATUS_CANCELING,
155
                     constants.JOB_STATUS_CANCELING)
156
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
157
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
158
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
159
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
160
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
161
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
162

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

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

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

    
181

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

    
195

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

    
200

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

    
205

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

    
210

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

    
215

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

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

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

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

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

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

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

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

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

    
271
  def testNoCertVerify(self):
272
    cfgfn = client.GenericCurlConfig()
273

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
345
    self.assertRaises(client.Error, cl._CreateCurl)
346

    
347
  def testCertVerifyFancySsl(self):
348
    certdir = "/tmp/some/UNUSED/cert/directory"
349
    pcverfn = _FakeFancySslPycurlVersion
350
    cfgfn = client.GenericCurlConfig(capath=certdir,
351
                                     _pycurl_version_fn=pcverfn)
352

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

    
357
    self.assertRaises(NotImplementedError, cl._CreateCurl)
358

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

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

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

    
373

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1374

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

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

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

    
1394
    return result
1395

    
1396

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