Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (51.1 kB)

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

    
4
# Copyright (C) 2010 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for unittesting the RAPI client module"""
23

    
24

    
25
import re
26
import unittest
27
import warnings
28
import pycurl
29

    
30
from ganeti import constants
31
from ganeti import http
32
from ganeti import serializer
33
from ganeti import utils
34
from ganeti import query
35
from ganeti import objects
36

    
37
from ganeti.rapi import connector
38
from ganeti.rapi import rlib2
39
from ganeti.rapi import client
40

    
41
import testutils
42

    
43

    
44
_URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
45

    
46
# List of resource handlers which aren't used by the RAPI client
47
_KNOWN_UNUSED = set([
48
  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_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
153
    self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
154
    self.assertEqual(client._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
155
    self.assertEqual(client._NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
156
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
157
    self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
158
    self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
159
    self.assertEqual(client.JOB_STATUS_CANCELING,
160
                     constants.JOB_STATUS_CANCELING)
161
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
162
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
163
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
164
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
165
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
166
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
167

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

    
171

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

    
185

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

    
190

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

    
195

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

    
200

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

    
205

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
363

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
899
    for sec in [True, None]:
900
      self.rapi.AddResponse(serializer.DumpJson([]))
901
      self.rapi.AddResponse(serializer.DumpJson([["res", "foo"]]))
902
      result = self.client.EvacuateNode("node-3", iallocator="hail",
903
                                        dry_run=True, accept_old=True,
904
                                        primary=False, secondary=sec)
905
      self.assertEqual(result, [["res", "foo"]])
906
      self.assertItems(["node-3"])
907
      self.assertQuery("iallocator", ["hail"])
908
      self.assertFalse(self.rapi.GetLastRequestData())
909
      self.assertDryRun()
910
      self.assertEqual(self.rapi.CountPending(), 0)
911

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1169
  def testRecreateInstanceDisks(self):
1170
    self.rapi.AddResponse("13553")
1171
    job_id = self.client.RecreateInstanceDisks("inst23153")
1172
    self.assertEqual(job_id, 13553)
1173
    self.assertItems(["inst23153"])
1174
    self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1175
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1176

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1352

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

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

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

    
1372
    return result
1373

    
1374

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