Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 1a2eb2dc

History | View | Annotate | Download (52.1 kB)

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

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

    
21

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

    
24

    
25
import unittest
26
import warnings
27
import pycurl
28

    
29
from ganeti import opcodes
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
from ganeti import rapi
37

    
38
import ganeti.rapi.testutils
39
from ganeti.rapi import connector
40
from ganeti.rapi import rlib2
41
from ganeti.rapi import client
42

    
43
import testutils
44

    
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
class RapiMock(object):
57
  def __init__(self):
58
    self._mapper = connector.Mapper()
59
    self._responses = []
60
    self._last_handler = None
61
    self._last_req_data = None
62

    
63
  def ResetResponses(self):
64
    del self._responses[:]
65

    
66
  def AddResponse(self, response, code=200):
67
    self._responses.insert(0, (code, response))
68

    
69
  def CountPending(self):
70
    return len(self._responses)
71

    
72
  def GetLastHandler(self):
73
    return self._last_handler
74

    
75
  def GetLastRequestData(self):
76
    return self._last_req_data
77

    
78
  def FetchResponse(self, path, method, headers, request_body):
79
    self._last_req_data = request_body
80

    
81
    try:
82
      (handler_cls, items, args) = self._mapper.getController(path)
83

    
84
      # Record handler as used
85
      _used_handlers.add(handler_cls)
86

    
87
      self._last_handler = handler_cls(items, args, None)
88
      if not hasattr(self._last_handler, method.upper()):
89
        raise http.HttpNotImplemented(message="Method not implemented")
90

    
91
    except http.HttpException, ex:
92
      code = ex.code
93
      response = ex.message
94
    else:
95
      if not self._responses:
96
        raise Exception("No responses")
97

    
98
      (code, response) = self._responses.pop()
99

    
100
    return (code, NotImplemented, response)
101

    
102

    
103
class TestConstants(unittest.TestCase):
104
  def test(self):
105
    self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
106
    self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
107
    self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
108
    self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
109
    self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
110
    self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
111
    self.assertEqual(client.JOB_STATUS_CANCELING,
112
                     constants.JOB_STATUS_CANCELING)
113
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
114
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
115
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
116
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
117
    self.assertEqual(client.JOB_STATUS_PENDING, constants.JOBS_PENDING)
118
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
119
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
120

    
121
    # Node evacuation
122
    self.assertEqual(client.NODE_EVAC_PRI, constants.NODE_EVAC_PRI)
123
    self.assertEqual(client.NODE_EVAC_SEC, constants.NODE_EVAC_SEC)
124
    self.assertEqual(client.NODE_EVAC_ALL, constants.NODE_EVAC_ALL)
125

    
126
    # Legacy name
127
    self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITING)
128

    
129
    # RAPI feature strings
130
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
131
    self.assertEqual(client.INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
132
    self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
133
    self.assertEqual(client.INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
134
    self.assertEqual(client._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
135
    self.assertEqual(client.NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
136
    self.assertEqual(client._NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
137
    self.assertEqual(client.NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
138

    
139

    
140
class RapiMockTest(unittest.TestCase):
141
  def test404(self):
142
    (code, _, body) = RapiMock().FetchResponse("/foo", "GET", None, None)
143
    self.assertEqual(code, 404)
144
    self.assertTrue(body is None)
145

    
146
  def test501(self):
147
    (code, _, body) = RapiMock().FetchResponse("/version", "POST", None, None)
148
    self.assertEqual(code, 501)
149
    self.assertEqual(body, "Method not implemented")
150

    
151
  def test200(self):
152
    rapi = RapiMock()
153
    rapi.AddResponse("2")
154
    (code, _, response) = rapi.FetchResponse("/version", "GET", None, None)
155
    self.assertEqual(200, code)
156
    self.assertEqual("2", response)
157
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
158

    
159

    
160
def _FakeNoSslPycurlVersion():
161
  # Note: incomplete version tuple
162
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
163

    
164

    
165
def _FakeFancySslPycurlVersion():
166
  # Note: incomplete version tuple
167
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
168

    
169

    
170
def _FakeOpenSslPycurlVersion():
171
  # Note: incomplete version tuple
172
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
173

    
174

    
175
def _FakeGnuTlsPycurlVersion():
176
  # Note: incomplete version tuple
177
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
178

    
179

    
180
class TestExtendedConfig(unittest.TestCase):
181
  def testAuth(self):
182
    cl = client.GanetiRapiClient("master.example.com",
183
      username="user", password="pw",
184
      curl_factory=lambda: rapi.testutils.FakeCurl(RapiMock()))
185

    
186
    curl = cl._CreateCurl()
187
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
188
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
189

    
190
  def testInvalidAuth(self):
191
    # No username
192
    self.assertRaises(client.Error, client.GanetiRapiClient,
193
                      "master-a.example.com", password="pw")
194
    # No password
195
    self.assertRaises(client.Error, client.GanetiRapiClient,
196
                      "master-b.example.com", username="user")
197

    
198
  def testCertVerifyInvalidCombinations(self):
199
    self.assertRaises(client.Error, client.GenericCurlConfig,
200
                      use_curl_cabundle=True, cafile="cert1.pem")
201
    self.assertRaises(client.Error, client.GenericCurlConfig,
202
                      use_curl_cabundle=True, capath="certs/")
203
    self.assertRaises(client.Error, client.GenericCurlConfig,
204
                      use_curl_cabundle=True,
205
                      cafile="cert1.pem", capath="certs/")
206

    
207
  def testProxySignalVerifyHostname(self):
208
    for use_gnutls in [False, True]:
209
      if use_gnutls:
210
        pcverfn = _FakeGnuTlsPycurlVersion
211
      else:
212
        pcverfn = _FakeOpenSslPycurlVersion
213

    
214
      for proxy in ["", "http://127.0.0.1:1234"]:
215
        for use_signal in [False, True]:
216
          for verify_hostname in [False, True]:
217
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
218
                                             verify_hostname=verify_hostname,
219
                                             _pycurl_version_fn=pcverfn)
220

    
221
            curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
222
            cl = client.GanetiRapiClient("master.example.com",
223
                                         curl_config_fn=cfgfn,
224
                                         curl_factory=curl_factory)
225

    
226
            curl = cl._CreateCurl()
227
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
228
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
229

    
230
            if verify_hostname:
231
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
232
            else:
233
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
234

    
235
  def testNoCertVerify(self):
236
    cfgfn = client.GenericCurlConfig()
237

    
238
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
239
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
240
                                 curl_factory=curl_factory)
241

    
242
    curl = cl._CreateCurl()
243
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
244
    self.assertFalse(curl.getopt(pycurl.CAINFO))
245
    self.assertFalse(curl.getopt(pycurl.CAPATH))
246

    
247
  def testCertVerifyCurlBundle(self):
248
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
249

    
250
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
251
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
252
                                 curl_factory=curl_factory)
253

    
254
    curl = cl._CreateCurl()
255
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
256
    self.assertFalse(curl.getopt(pycurl.CAINFO))
257
    self.assertFalse(curl.getopt(pycurl.CAPATH))
258

    
259
  def testCertVerifyCafile(self):
260
    mycert = "/tmp/some/UNUSED/cert/file.pem"
261
    cfgfn = client.GenericCurlConfig(cafile=mycert)
262

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

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

    
272
  def testCertVerifyCapath(self):
273
    certdir = "/tmp/some/UNUSED/cert/directory"
274
    pcverfn = _FakeOpenSslPycurlVersion
275
    cfgfn = client.GenericCurlConfig(capath=certdir,
276
                                     _pycurl_version_fn=pcverfn)
277

    
278
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
279
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
280
                                 curl_factory=curl_factory)
281

    
282
    curl = cl._CreateCurl()
283
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
284
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
285
    self.assertFalse(curl.getopt(pycurl.CAINFO))
286

    
287
  def testCertVerifyCapathGnuTls(self):
288
    certdir = "/tmp/some/UNUSED/cert/directory"
289
    pcverfn = _FakeGnuTlsPycurlVersion
290
    cfgfn = client.GenericCurlConfig(capath=certdir,
291
                                     _pycurl_version_fn=pcverfn)
292

    
293
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
294
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
295
                                 curl_factory=curl_factory)
296

    
297
    self.assertRaises(client.Error, cl._CreateCurl)
298

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

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

    
309
    self.assertRaises(client.Error, cl._CreateCurl)
310

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

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

    
321
    self.assertRaises(NotImplementedError, cl._CreateCurl)
322

    
323
  def testCertVerifyCapath(self):
324
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
325
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
326
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
327
                                         timeout=timeout)
328

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

    
333
        curl = cl._CreateCurl()
334
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
335
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
336

    
337

    
338
class GanetiRapiClientTests(testutils.GanetiTestCase):
339
  def setUp(self):
340
    testutils.GanetiTestCase.setUp(self)
341

    
342
    self.rapi = RapiMock()
343
    self.curl = rapi.testutils.FakeCurl(self.rapi)
344
    self.client = client.GanetiRapiClient("master.example.com",
345
                                          curl_factory=lambda: self.curl)
346

    
347
  def assertHandler(self, handler_cls):
348
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
349

    
350
  def assertQuery(self, key, value):
351
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
352

    
353
  def assertItems(self, items):
354
    self.assertEqual(items, self.rapi.GetLastHandler().items)
355

    
356
  def assertBulk(self):
357
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
358

    
359
  def assertDryRun(self):
360
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
361

    
362
  def assertUseForce(self):
363
    self.assertTrue(self.rapi.GetLastHandler().useForce())
364

    
365
  def testEncodeQuery(self):
366
    query = [
367
      ("a", None),
368
      ("b", 1),
369
      ("c", 2),
370
      ("d", "Foo"),
371
      ("e", True),
372
      ]
373

    
374
    expected = [
375
      ("a", ""),
376
      ("b", 1),
377
      ("c", 2),
378
      ("d", "Foo"),
379
      ("e", 1),
380
      ]
381

    
382
    self.assertEqualValues(self.client._EncodeQuery(query),
383
                           expected)
384

    
385
    # invalid types
386
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
387
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
388

    
389
  def testCurlSettings(self):
390
    self.rapi.AddResponse("2")
391
    self.assertEqual(2, self.client.GetVersion())
392
    self.assertHandler(rlib2.R_version)
393

    
394
    # Signals should be disabled by default
395
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
396

    
397
    # No auth and no proxy
398
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
399
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
400

    
401
    # Content-type is required for requests
402
    headers = self.curl.getopt(pycurl.HTTPHEADER)
403
    self.assert_("Content-type: application/json" in headers)
404

    
405
  def testHttpError(self):
406
    self.rapi.AddResponse(None, code=404)
407
    try:
408
      self.client.GetJobStatus(15140)
409
    except client.GanetiApiError, err:
410
      self.assertEqual(err.code, 404)
411
    else:
412
      self.fail("Didn't raise exception")
413

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

    
419
  def testGetFeatures(self):
420
    for features in [[], ["foo", "bar", "baz"]]:
421
      self.rapi.AddResponse(serializer.DumpJson(features))
422
      self.assertEqual(features, self.client.GetFeatures())
423
      self.assertHandler(rlib2.R_2_features)
424

    
425
  def testGetFeaturesNotFound(self):
426
    self.rapi.AddResponse(None, code=404)
427
    self.assertEqual([], self.client.GetFeatures())
428

    
429
  def testGetOperatingSystems(self):
430
    self.rapi.AddResponse("[\"beos\"]")
431
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
432
    self.assertHandler(rlib2.R_2_os)
433

    
434
  def testGetClusterTags(self):
435
    self.rapi.AddResponse("[\"tag\"]")
436
    self.assertEqual(["tag"], self.client.GetClusterTags())
437
    self.assertHandler(rlib2.R_2_tags)
438

    
439
  def testAddClusterTags(self):
440
    self.rapi.AddResponse("1234")
441
    self.assertEqual(1234,
442
        self.client.AddClusterTags(["awesome"], dry_run=True))
443
    self.assertHandler(rlib2.R_2_tags)
444
    self.assertDryRun()
445
    self.assertQuery("tag", ["awesome"])
446

    
447
  def testDeleteClusterTags(self):
448
    self.rapi.AddResponse("5107")
449
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
450
                                                         dry_run=True))
451
    self.assertHandler(rlib2.R_2_tags)
452
    self.assertDryRun()
453
    self.assertQuery("tag", ["awesome"])
454

    
455
  def testGetInfo(self):
456
    self.rapi.AddResponse("{}")
457
    self.assertEqual({}, self.client.GetInfo())
458
    self.assertHandler(rlib2.R_2_info)
459

    
460
  def testGetInstances(self):
461
    self.rapi.AddResponse("[]")
462
    self.assertEqual([], self.client.GetInstances(bulk=True))
463
    self.assertHandler(rlib2.R_2_instances)
464
    self.assertBulk()
465

    
466
  def testGetInstance(self):
467
    self.rapi.AddResponse("[]")
468
    self.assertEqual([], self.client.GetInstance("instance"))
469
    self.assertHandler(rlib2.R_2_instances_name)
470
    self.assertItems(["instance"])
471

    
472
  def testGetInstanceInfo(self):
473
    self.rapi.AddResponse("21291")
474
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
475
    self.assertHandler(rlib2.R_2_instances_name_info)
476
    self.assertItems(["inst3"])
477
    self.assertQuery("static", None)
478

    
479
    self.rapi.AddResponse("3428")
480
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
481
    self.assertHandler(rlib2.R_2_instances_name_info)
482
    self.assertItems(["inst31"])
483
    self.assertQuery("static", ["0"])
484

    
485
    self.rapi.AddResponse("15665")
486
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
487
    self.assertHandler(rlib2.R_2_instances_name_info)
488
    self.assertItems(["inst32"])
489
    self.assertQuery("static", ["1"])
490

    
491
  def testInstancesMultiAlloc(self):
492
    response = {
493
      constants.JOB_IDS_KEY: ["23423"],
494
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY: ["foobar"],
495
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: ["foobar2"],
496
      }
497
    self.rapi.AddResponse(serializer.DumpJson(response))
498
    insts = [self.client.InstanceAllocation("create", "foobar",
499
                                            "plain", [], []),
500
             self.client.InstanceAllocation("create", "foobar2",
501
                                            "drbd8", [{"size": 100}], [])]
502
    resp = self.client.InstancesMultiAlloc(insts)
503
    self.assertEqual(resp, response)
504
    self.assertHandler(rlib2.R_2_instances_multi_alloc)
505

    
506
  def testCreateInstanceOldVersion(self):
507
    # The old request format, version 0, is no longer supported
508
    self.rapi.AddResponse(None, code=404)
509
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
510
                      "create", "inst1.example.com", "plain", [], [])
511
    self.assertEqual(self.rapi.CountPending(), 0)
512

    
513
  def testCreateInstance(self):
514
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
515
    self.rapi.AddResponse("23030")
516
    job_id = self.client.CreateInstance("create", "inst1.example.com",
517
                                        "plain", [], [], dry_run=True)
518
    self.assertEqual(job_id, 23030)
519
    self.assertHandler(rlib2.R_2_instances)
520
    self.assertDryRun()
521

    
522
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
523

    
524
    for field in ["dry_run", "beparams", "hvparams", "start"]:
525
      self.assertFalse(field in data)
526

    
527
    self.assertEqual(data["name"], "inst1.example.com")
528
    self.assertEqual(data["disk_template"], "plain")
529

    
530
  def testCreateInstance2(self):
531
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
532
    self.rapi.AddResponse("24740")
533
    job_id = self.client.CreateInstance("import", "inst2.example.com",
534
                                        "drbd8", [{"size": 100,}],
535
                                        [{}, {"bridge": "br1", }],
536
                                        dry_run=False, start=True,
537
                                        pnode="node1", snode="node9",
538
                                        ip_check=False)
539
    self.assertEqual(job_id, 24740)
540
    self.assertHandler(rlib2.R_2_instances)
541

    
542
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
543
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
544
    self.assertEqual(data["name"], "inst2.example.com")
545
    self.assertEqual(data["disk_template"], "drbd8")
546
    self.assertEqual(data["start"], True)
547
    self.assertEqual(data["ip_check"], False)
548
    self.assertEqualValues(data["disks"], [{"size": 100,}])
549
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
550

    
551
  def testDeleteInstance(self):
552
    self.rapi.AddResponse("1234")
553
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
554
    self.assertHandler(rlib2.R_2_instances_name)
555
    self.assertItems(["instance"])
556
    self.assertDryRun()
557

    
558
  def testGetInstanceTags(self):
559
    self.rapi.AddResponse("[]")
560
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
561
    self.assertHandler(rlib2.R_2_instances_name_tags)
562
    self.assertItems(["fooinstance"])
563

    
564
  def testAddInstanceTags(self):
565
    self.rapi.AddResponse("1234")
566
    self.assertEqual(1234,
567
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
568
    self.assertHandler(rlib2.R_2_instances_name_tags)
569
    self.assertItems(["fooinstance"])
570
    self.assertDryRun()
571
    self.assertQuery("tag", ["awesome"])
572

    
573
  def testDeleteInstanceTags(self):
574
    self.rapi.AddResponse("25826")
575
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
576
                                                           dry_run=True))
577
    self.assertHandler(rlib2.R_2_instances_name_tags)
578
    self.assertItems(["foo"])
579
    self.assertDryRun()
580
    self.assertQuery("tag", ["awesome"])
581

    
582
  def testRebootInstance(self):
583
    self.rapi.AddResponse("6146")
584
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
585
                                        ignore_secondaries=True, dry_run=True)
586
    self.assertEqual(6146, job_id)
587
    self.assertHandler(rlib2.R_2_instances_name_reboot)
588
    self.assertItems(["i-bar"])
589
    self.assertDryRun()
590
    self.assertQuery("type", ["hard"])
591
    self.assertQuery("ignore_secondaries", ["1"])
592

    
593
  def testShutdownInstance(self):
594
    self.rapi.AddResponse("1487")
595
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
596
                                                        dry_run=True))
597
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
598
    self.assertItems(["foo-instance"])
599
    self.assertDryRun()
600

    
601
  def testStartupInstance(self):
602
    self.rapi.AddResponse("27149")
603
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
604
                                                        dry_run=True))
605
    self.assertHandler(rlib2.R_2_instances_name_startup)
606
    self.assertItems(["bar-instance"])
607
    self.assertDryRun()
608

    
609
  def testReinstallInstance(self):
610
    self.rapi.AddResponse(serializer.DumpJson([]))
611
    self.rapi.AddResponse("19119")
612
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
613
                                                          os="DOS",
614
                                                          no_startup=True))
615
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
616
    self.assertItems(["baz-instance"])
617
    self.assertQuery("os", ["DOS"])
618
    self.assertQuery("nostartup", ["1"])
619
    self.assertEqual(self.rapi.CountPending(), 0)
620

    
621
  def testReinstallInstanceNew(self):
622
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
623
    self.rapi.AddResponse("25689")
624
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
625
                                                          os="Debian",
626
                                                          no_startup=True))
627
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
628
    self.assertItems(["moo-instance"])
629
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
630
    self.assertEqual(len(data), 2)
631
    self.assertEqual(data["os"], "Debian")
632
    self.assertEqual(data["start"], False)
633
    self.assertEqual(self.rapi.CountPending(), 0)
634

    
635
  def testReinstallInstanceWithOsparams1(self):
636
    self.rapi.AddResponse(serializer.DumpJson([]))
637
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
638
                      "doo-instance", osparams={"x": "y"})
639
    self.assertEqual(self.rapi.CountPending(), 0)
640

    
641
  def testReinstallInstanceWithOsparams2(self):
642
    osparams = {
643
      "Hello": "World",
644
      "foo": "bar",
645
      }
646
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
647
    self.rapi.AddResponse("1717")
648
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
649
                                                         osparams=osparams))
650
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
651
    self.assertItems(["zoo-instance"])
652
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
653
    self.assertEqual(len(data), 2)
654
    self.assertEqual(data["osparams"], osparams)
655
    self.assertEqual(data["start"], True)
656
    self.assertEqual(self.rapi.CountPending(), 0)
657

    
658
  def testReplaceInstanceDisks(self):
659
    self.rapi.AddResponse("999")
660
    job_id = self.client.ReplaceInstanceDisks("instance-name",
661
        disks=[0, 1], iallocator="hail")
662
    self.assertEqual(999, job_id)
663
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
664
    self.assertItems(["instance-name"])
665
    self.assertQuery("disks", ["0,1"])
666
    self.assertQuery("mode", ["replace_auto"])
667
    self.assertQuery("iallocator", ["hail"])
668

    
669
    self.rapi.AddResponse("1000")
670
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
671
        disks=[1], mode="replace_on_secondary", remote_node="foo-node")
672
    self.assertEqual(1000, job_id)
673
    self.assertItems(["instance-bar"])
674
    self.assertQuery("disks", ["1"])
675
    self.assertQuery("remote_node", ["foo-node"])
676

    
677
    self.rapi.AddResponse("5175")
678
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
679
    self.assertItems(["instance-moo"])
680
    self.assertQuery("disks", None)
681

    
682
  def testPrepareExport(self):
683
    self.rapi.AddResponse("8326")
684
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
685
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
686
    self.assertItems(["inst1"])
687
    self.assertQuery("mode", ["local"])
688

    
689
  def testExportInstance(self):
690
    self.rapi.AddResponse("19695")
691
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
692
                                        shutdown=True)
693
    self.assertEqual(job_id, 19695)
694
    self.assertHandler(rlib2.R_2_instances_name_export)
695
    self.assertItems(["inst2"])
696

    
697
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
698
    self.assertEqual(data["mode"], "local")
699
    self.assertEqual(data["destination"], "nodeX")
700
    self.assertEqual(data["shutdown"], True)
701

    
702
  def testMigrateInstanceDefaults(self):
703
    self.rapi.AddResponse("24873")
704
    job_id = self.client.MigrateInstance("inst91")
705
    self.assertEqual(job_id, 24873)
706
    self.assertHandler(rlib2.R_2_instances_name_migrate)
707
    self.assertItems(["inst91"])
708

    
709
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
710
    self.assertFalse(data)
711

    
712
  def testMigrateInstance(self):
713
    for mode in constants.HT_MIGRATION_MODES:
714
      for cleanup in [False, True]:
715
        self.rapi.AddResponse("31910")
716
        job_id = self.client.MigrateInstance("inst289", mode=mode,
717
                                             cleanup=cleanup)
718
        self.assertEqual(job_id, 31910)
719
        self.assertHandler(rlib2.R_2_instances_name_migrate)
720
        self.assertItems(["inst289"])
721

    
722
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
723
        self.assertEqual(len(data), 2)
724
        self.assertEqual(data["mode"], mode)
725
        self.assertEqual(data["cleanup"], cleanup)
726

    
727
  def testFailoverInstanceDefaults(self):
728
    self.rapi.AddResponse("7639")
729
    job_id = self.client.FailoverInstance("inst13579")
730
    self.assertEqual(job_id, 7639)
731
    self.assertHandler(rlib2.R_2_instances_name_failover)
732
    self.assertItems(["inst13579"])
733

    
734
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
735
    self.assertFalse(data)
736

    
737
  def testFailoverInstance(self):
738
    for iallocator in ["dumb", "hail"]:
739
      for ignore_consistency in [False, True]:
740
        for target_node in ["node-a", "node2"]:
741
          self.rapi.AddResponse("19161")
742
          job_id = \
743
            self.client.FailoverInstance("inst251", iallocator=iallocator,
744
                                         ignore_consistency=ignore_consistency,
745
                                         target_node=target_node)
746
          self.assertEqual(job_id, 19161)
747
          self.assertHandler(rlib2.R_2_instances_name_failover)
748
          self.assertItems(["inst251"])
749

    
750
          data = serializer.LoadJson(self.rapi.GetLastRequestData())
751
          self.assertEqual(len(data), 3)
752
          self.assertEqual(data["iallocator"], iallocator)
753
          self.assertEqual(data["ignore_consistency"], ignore_consistency)
754
          self.assertEqual(data["target_node"], target_node)
755
          self.assertEqual(self.rapi.CountPending(), 0)
756

    
757
  def testRenameInstanceDefaults(self):
758
    new_name = "newnametha7euqu"
759
    self.rapi.AddResponse("8791")
760
    job_id = self.client.RenameInstance("inst18821", new_name)
761
    self.assertEqual(job_id, 8791)
762
    self.assertHandler(rlib2.R_2_instances_name_rename)
763
    self.assertItems(["inst18821"])
764

    
765
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
766
    self.assertEqualValues(data, {"new_name": new_name, })
767

    
768
  def testRenameInstance(self):
769
    new_name = "new-name-yiux1iin"
770
    for ip_check in [False, True]:
771
      for name_check in [False, True]:
772
        self.rapi.AddResponse("24776")
773
        job_id = self.client.RenameInstance("inst20967", new_name,
774
                                             ip_check=ip_check,
775
                                             name_check=name_check)
776
        self.assertEqual(job_id, 24776)
777
        self.assertHandler(rlib2.R_2_instances_name_rename)
778
        self.assertItems(["inst20967"])
779

    
780
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
781
        self.assertEqual(len(data), 3)
782
        self.assertEqual(data["new_name"], new_name)
783
        self.assertEqual(data["ip_check"], ip_check)
784
        self.assertEqual(data["name_check"], name_check)
785

    
786
  def testGetJobs(self):
787
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
788
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
789
    self.assertEqual([123, 124], self.client.GetJobs())
790
    self.assertHandler(rlib2.R_2_jobs)
791

    
792
  def testGetJobStatus(self):
793
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
794
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
795
    self.assertHandler(rlib2.R_2_jobs_id)
796
    self.assertItems(["1234"])
797

    
798
  def testWaitForJobChange(self):
799
    fields = ["id", "summary"]
800
    expected = {
801
      "job_info": [123, "something"],
802
      "log_entries": [],
803
      }
804

    
805
    self.rapi.AddResponse(serializer.DumpJson(expected))
806
    result = self.client.WaitForJobChange(123, fields, [], -1)
807
    self.assertEqualValues(expected, result)
808
    self.assertHandler(rlib2.R_2_jobs_id_wait)
809
    self.assertItems(["123"])
810

    
811
  def testCancelJob(self):
812
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
813
    self.assertEqual([True, "Job 123 will be canceled"],
814
                     self.client.CancelJob(999, dry_run=True))
815
    self.assertHandler(rlib2.R_2_jobs_id)
816
    self.assertItems(["999"])
817
    self.assertDryRun()
818

    
819
  def testGetNodes(self):
820
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
821
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
822
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
823
    self.assertHandler(rlib2.R_2_nodes)
824

    
825
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
826
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
827
    self.assertEqual([{"id": "node1", "uri": "uri1"},
828
                      {"id": "node2", "uri": "uri2"}],
829
                     self.client.GetNodes(bulk=True))
830
    self.assertHandler(rlib2.R_2_nodes)
831
    self.assertBulk()
832

    
833
  def testGetNode(self):
834
    self.rapi.AddResponse("{}")
835
    self.assertEqual({}, self.client.GetNode("node-foo"))
836
    self.assertHandler(rlib2.R_2_nodes_name)
837
    self.assertItems(["node-foo"])
838

    
839
  def testEvacuateNode(self):
840
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
841
    self.rapi.AddResponse("9876")
842
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
843
    self.assertEqual(9876, job_id)
844
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
845
    self.assertItems(["node-1"])
846
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
847
                     { "remote_node": "node-2", })
848
    self.assertEqual(self.rapi.CountPending(), 0)
849

    
850
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
851
    self.rapi.AddResponse("8888")
852
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
853
                                      mode=constants.NODE_EVAC_ALL,
854
                                      early_release=True)
855
    self.assertEqual(8888, job_id)
856
    self.assertItems(["node-3"])
857
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
858
      "iallocator": "hail",
859
      "mode": "all",
860
      "early_release": True,
861
      })
862
    self.assertDryRun()
863

    
864
    self.assertRaises(client.GanetiApiError,
865
                      self.client.EvacuateNode,
866
                      "node-4", iallocator="hail", remote_node="node-5")
867
    self.assertEqual(self.rapi.CountPending(), 0)
868

    
869
  def testEvacuateNodeOldResponse(self):
870
    self.rapi.AddResponse(serializer.DumpJson([]))
871
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
872
                      "node-4", accept_old=False)
873
    self.assertEqual(self.rapi.CountPending(), 0)
874

    
875
    for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
876
      self.rapi.AddResponse(serializer.DumpJson([]))
877
      self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
878
                        "node-4", accept_old=True, mode=mode)
879
      self.assertEqual(self.rapi.CountPending(), 0)
880

    
881
    self.rapi.AddResponse(serializer.DumpJson([]))
882
    self.rapi.AddResponse(serializer.DumpJson("21533"))
883
    result = self.client.EvacuateNode("node-3", iallocator="hail",
884
                                      dry_run=True, accept_old=True,
885
                                      mode=client.NODE_EVAC_SEC,
886
                                      early_release=True)
887
    self.assertEqual(result, "21533")
888
    self.assertItems(["node-3"])
889
    self.assertQuery("iallocator", ["hail"])
890
    self.assertQuery("early_release", ["1"])
891
    self.assertFalse(self.rapi.GetLastRequestData())
892
    self.assertDryRun()
893
    self.assertEqual(self.rapi.CountPending(), 0)
894

    
895
  def testMigrateNode(self):
896
    self.rapi.AddResponse(serializer.DumpJson([]))
897
    self.rapi.AddResponse("1111")
898
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
899
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
900
    self.assertItems(["node-a"])
901
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
902
    self.assertDryRun()
903
    self.assertFalse(self.rapi.GetLastRequestData())
904

    
905
    self.rapi.AddResponse(serializer.DumpJson([]))
906
    self.rapi.AddResponse("1112")
907
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
908
                                                   mode="live"))
909
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
910
    self.assertItems(["node-a"])
911
    self.assertQuery("mode", ["live"])
912
    self.assertDryRun()
913
    self.assertFalse(self.rapi.GetLastRequestData())
914

    
915
    self.rapi.AddResponse(serializer.DumpJson([]))
916
    self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
917
                      "node-c", target_node="foonode")
918
    self.assertEqual(self.rapi.CountPending(), 0)
919

    
920
  def testMigrateNodeBodyData(self):
921
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
922
    self.rapi.AddResponse("27539")
923
    self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
924
                                                    mode="live"))
925
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
926
    self.assertItems(["node-a"])
927
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
928
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
929
                     { "mode": "live", })
930

    
931
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
932
    self.rapi.AddResponse("14219")
933
    self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
934
                                                    target_node="node9",
935
                                                    iallocator="ial"))
936
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
937
    self.assertItems(["node-x"])
938
    self.assertDryRun()
939
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
940
                     { "target_node": "node9", "iallocator": "ial", })
941

    
942
    self.assertEqual(self.rapi.CountPending(), 0)
943

    
944
  def testGetNodeRole(self):
945
    self.rapi.AddResponse("\"master\"")
946
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
947
    self.assertHandler(rlib2.R_2_nodes_name_role)
948
    self.assertItems(["node-a"])
949

    
950
  def testSetNodeRole(self):
951
    self.rapi.AddResponse("789")
952
    self.assertEqual(789,
953
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
954
    self.assertHandler(rlib2.R_2_nodes_name_role)
955
    self.assertItems(["node-foo"])
956
    self.assertQuery("force", ["1"])
957
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
958

    
959
  def testPowercycleNode(self):
960
    self.rapi.AddResponse("23051")
961
    self.assertEqual(23051,
962
        self.client.PowercycleNode("node5468", force=True))
963
    self.assertHandler(rlib2.R_2_nodes_name_powercycle)
964
    self.assertItems(["node5468"])
965
    self.assertQuery("force", ["1"])
966
    self.assertFalse(self.rapi.GetLastRequestData())
967
    self.assertEqual(self.rapi.CountPending(), 0)
968

    
969
  def testModifyNode(self):
970
    self.rapi.AddResponse("3783")
971
    job_id = self.client.ModifyNode("node16979.example.com", drained=True)
972
    self.assertEqual(job_id, 3783)
973
    self.assertHandler(rlib2.R_2_nodes_name_modify)
974
    self.assertItems(["node16979.example.com"])
975
    self.assertEqual(self.rapi.CountPending(), 0)
976

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1353

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

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

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

    
1373
    return result
1374

    
1375

    
1376
if __name__ == "__main__":
1377
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)