Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 915faf72

History | View | Annotate | Download (51.9 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, 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_FINALIZED, constants.JOBS_FINALIZED)
118
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
119

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

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

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

    
138

    
139
class RapiMockTest(unittest.TestCase):
140
  def test(self):
141
    rapi = RapiMock()
142
    path = "/version"
143
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None, None))
144
    self.assertEqual((501, "Method not implemented"),
145
                     rapi.FetchResponse("/version", "POST", None, None))
146
    rapi.AddResponse("2")
147
    code, response = rapi.FetchResponse("/version", "GET", None, None)
148
    self.assertEqual(200, code)
149
    self.assertEqual("2", response)
150
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
151

    
152

    
153
def _FakeNoSslPycurlVersion():
154
  # Note: incomplete version tuple
155
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
156

    
157

    
158
def _FakeFancySslPycurlVersion():
159
  # Note: incomplete version tuple
160
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
161

    
162

    
163
def _FakeOpenSslPycurlVersion():
164
  # Note: incomplete version tuple
165
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
166

    
167

    
168
def _FakeGnuTlsPycurlVersion():
169
  # Note: incomplete version tuple
170
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
171

    
172

    
173
class TestExtendedConfig(unittest.TestCase):
174
  def testAuth(self):
175
    cl = client.GanetiRapiClient("master.example.com",
176
      username="user", password="pw",
177
      curl_factory=lambda: rapi.testutils.FakeCurl(RapiMock()))
178

    
179
    curl = cl._CreateCurl()
180
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
181
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
182

    
183
  def testInvalidAuth(self):
184
    # No username
185
    self.assertRaises(client.Error, client.GanetiRapiClient,
186
                      "master-a.example.com", password="pw")
187
    # No password
188
    self.assertRaises(client.Error, client.GanetiRapiClient,
189
                      "master-b.example.com", username="user")
190

    
191
  def testCertVerifyInvalidCombinations(self):
192
    self.assertRaises(client.Error, client.GenericCurlConfig,
193
                      use_curl_cabundle=True, cafile="cert1.pem")
194
    self.assertRaises(client.Error, client.GenericCurlConfig,
195
                      use_curl_cabundle=True, capath="certs/")
196
    self.assertRaises(client.Error, client.GenericCurlConfig,
197
                      use_curl_cabundle=True,
198
                      cafile="cert1.pem", capath="certs/")
199

    
200
  def testProxySignalVerifyHostname(self):
201
    for use_gnutls in [False, True]:
202
      if use_gnutls:
203
        pcverfn = _FakeGnuTlsPycurlVersion
204
      else:
205
        pcverfn = _FakeOpenSslPycurlVersion
206

    
207
      for proxy in ["", "http://127.0.0.1:1234"]:
208
        for use_signal in [False, True]:
209
          for verify_hostname in [False, True]:
210
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
211
                                             verify_hostname=verify_hostname,
212
                                             _pycurl_version_fn=pcverfn)
213

    
214
            curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
215
            cl = client.GanetiRapiClient("master.example.com",
216
                                         curl_config_fn=cfgfn,
217
                                         curl_factory=curl_factory)
218

    
219
            curl = cl._CreateCurl()
220
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
221
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
222

    
223
            if verify_hostname:
224
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
225
            else:
226
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
227

    
228
  def testNoCertVerify(self):
229
    cfgfn = client.GenericCurlConfig()
230

    
231
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
232
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
233
                                 curl_factory=curl_factory)
234

    
235
    curl = cl._CreateCurl()
236
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
237
    self.assertFalse(curl.getopt(pycurl.CAINFO))
238
    self.assertFalse(curl.getopt(pycurl.CAPATH))
239

    
240
  def testCertVerifyCurlBundle(self):
241
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
242

    
243
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
244
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
245
                                 curl_factory=curl_factory)
246

    
247
    curl = cl._CreateCurl()
248
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
249
    self.assertFalse(curl.getopt(pycurl.CAINFO))
250
    self.assertFalse(curl.getopt(pycurl.CAPATH))
251

    
252
  def testCertVerifyCafile(self):
253
    mycert = "/tmp/some/UNUSED/cert/file.pem"
254
    cfgfn = client.GenericCurlConfig(cafile=mycert)
255

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

    
260
    curl = cl._CreateCurl()
261
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
262
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
263
    self.assertFalse(curl.getopt(pycurl.CAPATH))
264

    
265
  def testCertVerifyCapath(self):
266
    certdir = "/tmp/some/UNUSED/cert/directory"
267
    pcverfn = _FakeOpenSslPycurlVersion
268
    cfgfn = client.GenericCurlConfig(capath=certdir,
269
                                     _pycurl_version_fn=pcverfn)
270

    
271
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
272
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
273
                                 curl_factory=curl_factory)
274

    
275
    curl = cl._CreateCurl()
276
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
277
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
278
    self.assertFalse(curl.getopt(pycurl.CAINFO))
279

    
280
  def testCertVerifyCapathGnuTls(self):
281
    certdir = "/tmp/some/UNUSED/cert/directory"
282
    pcverfn = _FakeGnuTlsPycurlVersion
283
    cfgfn = client.GenericCurlConfig(capath=certdir,
284
                                     _pycurl_version_fn=pcverfn)
285

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

    
290
    self.assertRaises(client.Error, cl._CreateCurl)
291

    
292
  def testCertVerifyNoSsl(self):
293
    certdir = "/tmp/some/UNUSED/cert/directory"
294
    pcverfn = _FakeNoSslPycurlVersion
295
    cfgfn = client.GenericCurlConfig(capath=certdir,
296
                                     _pycurl_version_fn=pcverfn)
297

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

    
302
    self.assertRaises(client.Error, cl._CreateCurl)
303

    
304
  def testCertVerifyFancySsl(self):
305
    certdir = "/tmp/some/UNUSED/cert/directory"
306
    pcverfn = _FakeFancySslPycurlVersion
307
    cfgfn = client.GenericCurlConfig(capath=certdir,
308
                                     _pycurl_version_fn=pcverfn)
309

    
310
    curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
311
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
312
                                 curl_factory=curl_factory)
313

    
314
    self.assertRaises(NotImplementedError, cl._CreateCurl)
315

    
316
  def testCertVerifyCapath(self):
317
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
318
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
319
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
320
                                         timeout=timeout)
321

    
322
        curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
323
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
324
                                     curl_factory=curl_factory)
325

    
326
        curl = cl._CreateCurl()
327
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
328
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
329

    
330

    
331
class GanetiRapiClientTests(testutils.GanetiTestCase):
332
  def setUp(self):
333
    testutils.GanetiTestCase.setUp(self)
334

    
335
    self.rapi = RapiMock()
336
    self.curl = rapi.testutils.FakeCurl(self.rapi)
337
    self.client = client.GanetiRapiClient("master.example.com",
338
                                          curl_factory=lambda: self.curl)
339

    
340
  def assertHandler(self, handler_cls):
341
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
342

    
343
  def assertQuery(self, key, value):
344
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
345

    
346
  def assertItems(self, items):
347
    self.assertEqual(items, self.rapi.GetLastHandler().items)
348

    
349
  def assertBulk(self):
350
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
351

    
352
  def assertDryRun(self):
353
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
354

    
355
  def assertUseForce(self):
356
    self.assertTrue(self.rapi.GetLastHandler().useForce())
357

    
358
  def testEncodeQuery(self):
359
    query = [
360
      ("a", None),
361
      ("b", 1),
362
      ("c", 2),
363
      ("d", "Foo"),
364
      ("e", True),
365
      ]
366

    
367
    expected = [
368
      ("a", ""),
369
      ("b", 1),
370
      ("c", 2),
371
      ("d", "Foo"),
372
      ("e", 1),
373
      ]
374

    
375
    self.assertEqualValues(self.client._EncodeQuery(query),
376
                           expected)
377

    
378
    # invalid types
379
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
380
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
381

    
382
  def testCurlSettings(self):
383
    self.rapi.AddResponse("2")
384
    self.assertEqual(2, self.client.GetVersion())
385
    self.assertHandler(rlib2.R_version)
386

    
387
    # Signals should be disabled by default
388
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
389

    
390
    # No auth and no proxy
391
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
392
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
393

    
394
    # Content-type is required for requests
395
    headers = self.curl.getopt(pycurl.HTTPHEADER)
396
    self.assert_("Content-type: application/json" in headers)
397

    
398
  def testHttpError(self):
399
    self.rapi.AddResponse(None, code=404)
400
    try:
401
      self.client.GetJobStatus(15140)
402
    except client.GanetiApiError, err:
403
      self.assertEqual(err.code, 404)
404
    else:
405
      self.fail("Didn't raise exception")
406

    
407
  def testGetVersion(self):
408
    self.rapi.AddResponse("2")
409
    self.assertEqual(2, self.client.GetVersion())
410
    self.assertHandler(rlib2.R_version)
411

    
412
  def testGetFeatures(self):
413
    for features in [[], ["foo", "bar", "baz"]]:
414
      self.rapi.AddResponse(serializer.DumpJson(features))
415
      self.assertEqual(features, self.client.GetFeatures())
416
      self.assertHandler(rlib2.R_2_features)
417

    
418
  def testGetFeaturesNotFound(self):
419
    self.rapi.AddResponse(None, code=404)
420
    self.assertEqual([], self.client.GetFeatures())
421

    
422
  def testGetOperatingSystems(self):
423
    self.rapi.AddResponse("[\"beos\"]")
424
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
425
    self.assertHandler(rlib2.R_2_os)
426

    
427
  def testGetClusterTags(self):
428
    self.rapi.AddResponse("[\"tag\"]")
429
    self.assertEqual(["tag"], self.client.GetClusterTags())
430
    self.assertHandler(rlib2.R_2_tags)
431

    
432
  def testAddClusterTags(self):
433
    self.rapi.AddResponse("1234")
434
    self.assertEqual(1234,
435
        self.client.AddClusterTags(["awesome"], dry_run=True))
436
    self.assertHandler(rlib2.R_2_tags)
437
    self.assertDryRun()
438
    self.assertQuery("tag", ["awesome"])
439

    
440
  def testDeleteClusterTags(self):
441
    self.rapi.AddResponse("5107")
442
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
443
                                                         dry_run=True))
444
    self.assertHandler(rlib2.R_2_tags)
445
    self.assertDryRun()
446
    self.assertQuery("tag", ["awesome"])
447

    
448
  def testGetInfo(self):
449
    self.rapi.AddResponse("{}")
450
    self.assertEqual({}, self.client.GetInfo())
451
    self.assertHandler(rlib2.R_2_info)
452

    
453
  def testGetInstances(self):
454
    self.rapi.AddResponse("[]")
455
    self.assertEqual([], self.client.GetInstances(bulk=True))
456
    self.assertHandler(rlib2.R_2_instances)
457
    self.assertBulk()
458

    
459
  def testGetInstance(self):
460
    self.rapi.AddResponse("[]")
461
    self.assertEqual([], self.client.GetInstance("instance"))
462
    self.assertHandler(rlib2.R_2_instances_name)
463
    self.assertItems(["instance"])
464

    
465
  def testGetInstanceInfo(self):
466
    self.rapi.AddResponse("21291")
467
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
468
    self.assertHandler(rlib2.R_2_instances_name_info)
469
    self.assertItems(["inst3"])
470
    self.assertQuery("static", None)
471

    
472
    self.rapi.AddResponse("3428")
473
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
474
    self.assertHandler(rlib2.R_2_instances_name_info)
475
    self.assertItems(["inst31"])
476
    self.assertQuery("static", ["0"])
477

    
478
    self.rapi.AddResponse("15665")
479
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
480
    self.assertHandler(rlib2.R_2_instances_name_info)
481
    self.assertItems(["inst32"])
482
    self.assertQuery("static", ["1"])
483

    
484
  def testInstancesMultiAlloc(self):
485
    response = {
486
      constants.JOB_IDS_KEY: ["23423"],
487
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY: ["foobar"],
488
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: ["foobar2"],
489
      }
490
    self.rapi.AddResponse(serializer.DumpJson(response))
491
    insts = [self.client.InstanceAllocation("create", "foobar",
492
                                            "plain", [], []),
493
             self.client.InstanceAllocation("create", "foobar2",
494
                                            "drbd8", [{"size": 100}], [])]
495
    resp = self.client.InstancesMultiAlloc(insts)
496
    self.assertEqual(resp, response)
497
    self.assertHandler(rlib2.R_2_instances_multi_alloc)
498

    
499
  def testCreateInstanceOldVersion(self):
500
    # The old request format, version 0, is no longer supported
501
    self.rapi.AddResponse(None, code=404)
502
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
503
                      "create", "inst1.example.com", "plain", [], [])
504
    self.assertEqual(self.rapi.CountPending(), 0)
505

    
506
  def testCreateInstance(self):
507
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
508
    self.rapi.AddResponse("23030")
509
    job_id = self.client.CreateInstance("create", "inst1.example.com",
510
                                        "plain", [], [], dry_run=True)
511
    self.assertEqual(job_id, 23030)
512
    self.assertHandler(rlib2.R_2_instances)
513
    self.assertDryRun()
514

    
515
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
516

    
517
    for field in ["dry_run", "beparams", "hvparams", "start"]:
518
      self.assertFalse(field in data)
519

    
520
    self.assertEqual(data["name"], "inst1.example.com")
521
    self.assertEqual(data["disk_template"], "plain")
522

    
523
  def testCreateInstance2(self):
524
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
525
    self.rapi.AddResponse("24740")
526
    job_id = self.client.CreateInstance("import", "inst2.example.com",
527
                                        "drbd8", [{"size": 100,}],
528
                                        [{}, {"bridge": "br1", }],
529
                                        dry_run=False, start=True,
530
                                        pnode="node1", snode="node9",
531
                                        ip_check=False)
532
    self.assertEqual(job_id, 24740)
533
    self.assertHandler(rlib2.R_2_instances)
534

    
535
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
536
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
537
    self.assertEqual(data["name"], "inst2.example.com")
538
    self.assertEqual(data["disk_template"], "drbd8")
539
    self.assertEqual(data["start"], True)
540
    self.assertEqual(data["ip_check"], False)
541
    self.assertEqualValues(data["disks"], [{"size": 100,}])
542
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
543

    
544
  def testDeleteInstance(self):
545
    self.rapi.AddResponse("1234")
546
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
547
    self.assertHandler(rlib2.R_2_instances_name)
548
    self.assertItems(["instance"])
549
    self.assertDryRun()
550

    
551
  def testGetInstanceTags(self):
552
    self.rapi.AddResponse("[]")
553
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
554
    self.assertHandler(rlib2.R_2_instances_name_tags)
555
    self.assertItems(["fooinstance"])
556

    
557
  def testAddInstanceTags(self):
558
    self.rapi.AddResponse("1234")
559
    self.assertEqual(1234,
560
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
561
    self.assertHandler(rlib2.R_2_instances_name_tags)
562
    self.assertItems(["fooinstance"])
563
    self.assertDryRun()
564
    self.assertQuery("tag", ["awesome"])
565

    
566
  def testDeleteInstanceTags(self):
567
    self.rapi.AddResponse("25826")
568
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
569
                                                           dry_run=True))
570
    self.assertHandler(rlib2.R_2_instances_name_tags)
571
    self.assertItems(["foo"])
572
    self.assertDryRun()
573
    self.assertQuery("tag", ["awesome"])
574

    
575
  def testRebootInstance(self):
576
    self.rapi.AddResponse("6146")
577
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
578
                                        ignore_secondaries=True, dry_run=True)
579
    self.assertEqual(6146, job_id)
580
    self.assertHandler(rlib2.R_2_instances_name_reboot)
581
    self.assertItems(["i-bar"])
582
    self.assertDryRun()
583
    self.assertQuery("type", ["hard"])
584
    self.assertQuery("ignore_secondaries", ["1"])
585

    
586
  def testShutdownInstance(self):
587
    self.rapi.AddResponse("1487")
588
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
589
                                                        dry_run=True))
590
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
591
    self.assertItems(["foo-instance"])
592
    self.assertDryRun()
593

    
594
  def testStartupInstance(self):
595
    self.rapi.AddResponse("27149")
596
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
597
                                                        dry_run=True))
598
    self.assertHandler(rlib2.R_2_instances_name_startup)
599
    self.assertItems(["bar-instance"])
600
    self.assertDryRun()
601

    
602
  def testReinstallInstance(self):
603
    self.rapi.AddResponse(serializer.DumpJson([]))
604
    self.rapi.AddResponse("19119")
605
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
606
                                                          os="DOS",
607
                                                          no_startup=True))
608
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
609
    self.assertItems(["baz-instance"])
610
    self.assertQuery("os", ["DOS"])
611
    self.assertQuery("nostartup", ["1"])
612
    self.assertEqual(self.rapi.CountPending(), 0)
613

    
614
  def testReinstallInstanceNew(self):
615
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
616
    self.rapi.AddResponse("25689")
617
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
618
                                                          os="Debian",
619
                                                          no_startup=True))
620
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
621
    self.assertItems(["moo-instance"])
622
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
623
    self.assertEqual(len(data), 2)
624
    self.assertEqual(data["os"], "Debian")
625
    self.assertEqual(data["start"], False)
626
    self.assertEqual(self.rapi.CountPending(), 0)
627

    
628
  def testReinstallInstanceWithOsparams1(self):
629
    self.rapi.AddResponse(serializer.DumpJson([]))
630
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
631
                      "doo-instance", osparams={"x": "y"})
632
    self.assertEqual(self.rapi.CountPending(), 0)
633

    
634
  def testReinstallInstanceWithOsparams2(self):
635
    osparams = {
636
      "Hello": "World",
637
      "foo": "bar",
638
      }
639
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
640
    self.rapi.AddResponse("1717")
641
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
642
                                                         osparams=osparams))
643
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
644
    self.assertItems(["zoo-instance"])
645
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
646
    self.assertEqual(len(data), 2)
647
    self.assertEqual(data["osparams"], osparams)
648
    self.assertEqual(data["start"], True)
649
    self.assertEqual(self.rapi.CountPending(), 0)
650

    
651
  def testReplaceInstanceDisks(self):
652
    self.rapi.AddResponse("999")
653
    job_id = self.client.ReplaceInstanceDisks("instance-name",
654
        disks=[0, 1], iallocator="hail")
655
    self.assertEqual(999, job_id)
656
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
657
    self.assertItems(["instance-name"])
658
    self.assertQuery("disks", ["0,1"])
659
    self.assertQuery("mode", ["replace_auto"])
660
    self.assertQuery("iallocator", ["hail"])
661

    
662
    self.rapi.AddResponse("1000")
663
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
664
        disks=[1], mode="replace_on_secondary", remote_node="foo-node")
665
    self.assertEqual(1000, job_id)
666
    self.assertItems(["instance-bar"])
667
    self.assertQuery("disks", ["1"])
668
    self.assertQuery("remote_node", ["foo-node"])
669

    
670
    self.rapi.AddResponse("5175")
671
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
672
    self.assertItems(["instance-moo"])
673
    self.assertQuery("disks", None)
674

    
675
  def testPrepareExport(self):
676
    self.rapi.AddResponse("8326")
677
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
678
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
679
    self.assertItems(["inst1"])
680
    self.assertQuery("mode", ["local"])
681

    
682
  def testExportInstance(self):
683
    self.rapi.AddResponse("19695")
684
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
685
                                        shutdown=True)
686
    self.assertEqual(job_id, 19695)
687
    self.assertHandler(rlib2.R_2_instances_name_export)
688
    self.assertItems(["inst2"])
689

    
690
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
691
    self.assertEqual(data["mode"], "local")
692
    self.assertEqual(data["destination"], "nodeX")
693
    self.assertEqual(data["shutdown"], True)
694

    
695
  def testMigrateInstanceDefaults(self):
696
    self.rapi.AddResponse("24873")
697
    job_id = self.client.MigrateInstance("inst91")
698
    self.assertEqual(job_id, 24873)
699
    self.assertHandler(rlib2.R_2_instances_name_migrate)
700
    self.assertItems(["inst91"])
701

    
702
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
703
    self.assertFalse(data)
704

    
705
  def testMigrateInstance(self):
706
    for mode in constants.HT_MIGRATION_MODES:
707
      for cleanup in [False, True]:
708
        self.rapi.AddResponse("31910")
709
        job_id = self.client.MigrateInstance("inst289", mode=mode,
710
                                             cleanup=cleanup)
711
        self.assertEqual(job_id, 31910)
712
        self.assertHandler(rlib2.R_2_instances_name_migrate)
713
        self.assertItems(["inst289"])
714

    
715
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
716
        self.assertEqual(len(data), 2)
717
        self.assertEqual(data["mode"], mode)
718
        self.assertEqual(data["cleanup"], cleanup)
719

    
720
  def testFailoverInstanceDefaults(self):
721
    self.rapi.AddResponse("7639")
722
    job_id = self.client.FailoverInstance("inst13579")
723
    self.assertEqual(job_id, 7639)
724
    self.assertHandler(rlib2.R_2_instances_name_failover)
725
    self.assertItems(["inst13579"])
726

    
727
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
728
    self.assertFalse(data)
729

    
730
  def testFailoverInstance(self):
731
    for iallocator in ["dumb", "hail"]:
732
      for ignore_consistency in [False, True]:
733
        for target_node in ["node-a", "node2"]:
734
          self.rapi.AddResponse("19161")
735
          job_id = \
736
            self.client.FailoverInstance("inst251", iallocator=iallocator,
737
                                         ignore_consistency=ignore_consistency,
738
                                         target_node=target_node)
739
          self.assertEqual(job_id, 19161)
740
          self.assertHandler(rlib2.R_2_instances_name_failover)
741
          self.assertItems(["inst251"])
742

    
743
          data = serializer.LoadJson(self.rapi.GetLastRequestData())
744
          self.assertEqual(len(data), 3)
745
          self.assertEqual(data["iallocator"], iallocator)
746
          self.assertEqual(data["ignore_consistency"], ignore_consistency)
747
          self.assertEqual(data["target_node"], target_node)
748
          self.assertEqual(self.rapi.CountPending(), 0)
749

    
750
  def testRenameInstanceDefaults(self):
751
    new_name = "newnametha7euqu"
752
    self.rapi.AddResponse("8791")
753
    job_id = self.client.RenameInstance("inst18821", new_name)
754
    self.assertEqual(job_id, 8791)
755
    self.assertHandler(rlib2.R_2_instances_name_rename)
756
    self.assertItems(["inst18821"])
757

    
758
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
759
    self.assertEqualValues(data, {"new_name": new_name, })
760

    
761
  def testRenameInstance(self):
762
    new_name = "new-name-yiux1iin"
763
    for ip_check in [False, True]:
764
      for name_check in [False, True]:
765
        self.rapi.AddResponse("24776")
766
        job_id = self.client.RenameInstance("inst20967", new_name,
767
                                             ip_check=ip_check,
768
                                             name_check=name_check)
769
        self.assertEqual(job_id, 24776)
770
        self.assertHandler(rlib2.R_2_instances_name_rename)
771
        self.assertItems(["inst20967"])
772

    
773
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
774
        self.assertEqual(len(data), 3)
775
        self.assertEqual(data["new_name"], new_name)
776
        self.assertEqual(data["ip_check"], ip_check)
777
        self.assertEqual(data["name_check"], name_check)
778

    
779
  def testGetJobs(self):
780
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
781
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
782
    self.assertEqual([123, 124], self.client.GetJobs())
783
    self.assertHandler(rlib2.R_2_jobs)
784

    
785
  def testGetJobStatus(self):
786
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
787
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
788
    self.assertHandler(rlib2.R_2_jobs_id)
789
    self.assertItems(["1234"])
790

    
791
  def testWaitForJobChange(self):
792
    fields = ["id", "summary"]
793
    expected = {
794
      "job_info": [123, "something"],
795
      "log_entries": [],
796
      }
797

    
798
    self.rapi.AddResponse(serializer.DumpJson(expected))
799
    result = self.client.WaitForJobChange(123, fields, [], -1)
800
    self.assertEqualValues(expected, result)
801
    self.assertHandler(rlib2.R_2_jobs_id_wait)
802
    self.assertItems(["123"])
803

    
804
  def testCancelJob(self):
805
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
806
    self.assertEqual([True, "Job 123 will be canceled"],
807
                     self.client.CancelJob(999, dry_run=True))
808
    self.assertHandler(rlib2.R_2_jobs_id)
809
    self.assertItems(["999"])
810
    self.assertDryRun()
811

    
812
  def testGetNodes(self):
813
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
814
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
815
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
816
    self.assertHandler(rlib2.R_2_nodes)
817

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

    
826
  def testGetNode(self):
827
    self.rapi.AddResponse("{}")
828
    self.assertEqual({}, self.client.GetNode("node-foo"))
829
    self.assertHandler(rlib2.R_2_nodes_name)
830
    self.assertItems(["node-foo"])
831

    
832
  def testEvacuateNode(self):
833
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
834
    self.rapi.AddResponse("9876")
835
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
836
    self.assertEqual(9876, job_id)
837
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
838
    self.assertItems(["node-1"])
839
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
840
                     { "remote_node": "node-2", })
841
    self.assertEqual(self.rapi.CountPending(), 0)
842

    
843
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
844
    self.rapi.AddResponse("8888")
845
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
846
                                      mode=constants.NODE_EVAC_ALL,
847
                                      early_release=True)
848
    self.assertEqual(8888, job_id)
849
    self.assertItems(["node-3"])
850
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
851
      "iallocator": "hail",
852
      "mode": "all",
853
      "early_release": True,
854
      })
855
    self.assertDryRun()
856

    
857
    self.assertRaises(client.GanetiApiError,
858
                      self.client.EvacuateNode,
859
                      "node-4", iallocator="hail", remote_node="node-5")
860
    self.assertEqual(self.rapi.CountPending(), 0)
861

    
862
  def testEvacuateNodeOldResponse(self):
863
    self.rapi.AddResponse(serializer.DumpJson([]))
864
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
865
                      "node-4", accept_old=False)
866
    self.assertEqual(self.rapi.CountPending(), 0)
867

    
868
    for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
869
      self.rapi.AddResponse(serializer.DumpJson([]))
870
      self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
871
                        "node-4", accept_old=True, mode=mode)
872
      self.assertEqual(self.rapi.CountPending(), 0)
873

    
874
    self.rapi.AddResponse(serializer.DumpJson([]))
875
    self.rapi.AddResponse(serializer.DumpJson("21533"))
876
    result = self.client.EvacuateNode("node-3", iallocator="hail",
877
                                      dry_run=True, accept_old=True,
878
                                      mode=client.NODE_EVAC_SEC,
879
                                      early_release=True)
880
    self.assertEqual(result, "21533")
881
    self.assertItems(["node-3"])
882
    self.assertQuery("iallocator", ["hail"])
883
    self.assertQuery("early_release", ["1"])
884
    self.assertFalse(self.rapi.GetLastRequestData())
885
    self.assertDryRun()
886
    self.assertEqual(self.rapi.CountPending(), 0)
887

    
888
  def testMigrateNode(self):
889
    self.rapi.AddResponse(serializer.DumpJson([]))
890
    self.rapi.AddResponse("1111")
891
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
892
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
893
    self.assertItems(["node-a"])
894
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
895
    self.assertDryRun()
896
    self.assertFalse(self.rapi.GetLastRequestData())
897

    
898
    self.rapi.AddResponse(serializer.DumpJson([]))
899
    self.rapi.AddResponse("1112")
900
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
901
                                                   mode="live"))
902
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
903
    self.assertItems(["node-a"])
904
    self.assertQuery("mode", ["live"])
905
    self.assertDryRun()
906
    self.assertFalse(self.rapi.GetLastRequestData())
907

    
908
    self.rapi.AddResponse(serializer.DumpJson([]))
909
    self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
910
                      "node-c", target_node="foonode")
911
    self.assertEqual(self.rapi.CountPending(), 0)
912

    
913
  def testMigrateNodeBodyData(self):
914
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
915
    self.rapi.AddResponse("27539")
916
    self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
917
                                                    mode="live"))
918
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
919
    self.assertItems(["node-a"])
920
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
921
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
922
                     { "mode": "live", })
923

    
924
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
925
    self.rapi.AddResponse("14219")
926
    self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
927
                                                    target_node="node9",
928
                                                    iallocator="ial"))
929
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
930
    self.assertItems(["node-x"])
931
    self.assertDryRun()
932
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
933
                     { "target_node": "node9", "iallocator": "ial", })
934

    
935
    self.assertEqual(self.rapi.CountPending(), 0)
936

    
937
  def testGetNodeRole(self):
938
    self.rapi.AddResponse("\"master\"")
939
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
940
    self.assertHandler(rlib2.R_2_nodes_name_role)
941
    self.assertItems(["node-a"])
942

    
943
  def testSetNodeRole(self):
944
    self.rapi.AddResponse("789")
945
    self.assertEqual(789,
946
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
947
    self.assertHandler(rlib2.R_2_nodes_name_role)
948
    self.assertItems(["node-foo"])
949
    self.assertQuery("force", ["1"])
950
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
951

    
952
  def testPowercycleNode(self):
953
    self.rapi.AddResponse("23051")
954
    self.assertEqual(23051,
955
        self.client.PowercycleNode("node5468", force=True))
956
    self.assertHandler(rlib2.R_2_nodes_name_powercycle)
957
    self.assertItems(["node5468"])
958
    self.assertQuery("force", ["1"])
959
    self.assertFalse(self.rapi.GetLastRequestData())
960
    self.assertEqual(self.rapi.CountPending(), 0)
961

    
962
  def testModifyNode(self):
963
    self.rapi.AddResponse("3783")
964
    job_id = self.client.ModifyNode("node16979.example.com", drained=True)
965
    self.assertEqual(job_id, 3783)
966
    self.assertHandler(rlib2.R_2_nodes_name_modify)
967
    self.assertItems(["node16979.example.com"])
968
    self.assertEqual(self.rapi.CountPending(), 0)
969

    
970
  def testGetNodeStorageUnits(self):
971
    self.rapi.AddResponse("42")
972
    self.assertEqual(42,
973
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
974
    self.assertHandler(rlib2.R_2_nodes_name_storage)
975
    self.assertItems(["node-x"])
976
    self.assertQuery("storage_type", ["lvm-pv"])
977
    self.assertQuery("output_fields", ["fields"])
978

    
979
  def testModifyNodeStorageUnits(self):
980
    self.rapi.AddResponse("14")
981
    self.assertEqual(14,
982
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
983
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
984
    self.assertItems(["node-z"])
985
    self.assertQuery("storage_type", ["lvm-pv"])
986
    self.assertQuery("name", ["hda"])
987
    self.assertQuery("allocatable", None)
988

    
989
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
990
      self.rapi.AddResponse("7205")
991
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
992
                                                  allocatable=allocatable)
993
      self.assertEqual(7205, job_id)
994
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
995
      self.assertItems(["node-z"])
996
      self.assertQuery("storage_type", ["lvm-pv"])
997
      self.assertQuery("name", ["hda"])
998
      self.assertQuery("allocatable", [query_allocatable])
999

    
1000
  def testRepairNodeStorageUnits(self):
1001
    self.rapi.AddResponse("99")
1002
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1003
                                                            "hda"))
1004
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1005
    self.assertItems(["node-z"])
1006
    self.assertQuery("storage_type", ["lvm-pv"])
1007
    self.assertQuery("name", ["hda"])
1008

    
1009
  def testGetNodeTags(self):
1010
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
1011
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1012
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1013
    self.assertItems(["node-k"])
1014

    
1015
  def testAddNodeTags(self):
1016
    self.rapi.AddResponse("1234")
1017
    self.assertEqual(1234,
1018
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1019
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1020
    self.assertItems(["node-v"])
1021
    self.assertDryRun()
1022
    self.assertQuery("tag", ["awesome"])
1023

    
1024
  def testDeleteNodeTags(self):
1025
    self.rapi.AddResponse("16861")
1026
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1027
                                                       dry_run=True))
1028
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1029
    self.assertItems(["node-w"])
1030
    self.assertDryRun()
1031
    self.assertQuery("tag", ["awesome"])
1032

    
1033
  def testGetGroups(self):
1034
    groups = [{"name": "group1",
1035
               "uri": "/2/groups/group1",
1036
               },
1037
              {"name": "group2",
1038
               "uri": "/2/groups/group2",
1039
               },
1040
              ]
1041
    self.rapi.AddResponse(serializer.DumpJson(groups))
1042
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
1043
    self.assertHandler(rlib2.R_2_groups)
1044

    
1045
  def testGetGroupsBulk(self):
1046
    groups = [{"name": "group1",
1047
               "uri": "/2/groups/group1",
1048
               "node_cnt": 2,
1049
               "node_list": ["gnt1.test",
1050
                             "gnt2.test",
1051
                             ],
1052
               },
1053
              {"name": "group2",
1054
               "uri": "/2/groups/group2",
1055
               "node_cnt": 1,
1056
               "node_list": ["gnt3.test",
1057
                             ],
1058
               },
1059
              ]
1060
    self.rapi.AddResponse(serializer.DumpJson(groups))
1061

    
1062
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
1063
    self.assertHandler(rlib2.R_2_groups)
1064
    self.assertBulk()
1065

    
1066
  def testGetGroup(self):
1067
    group = {"ctime": None,
1068
             "name": "default",
1069
             }
1070
    self.rapi.AddResponse(serializer.DumpJson(group))
1071
    self.assertEqual({"ctime": None, "name": "default"},
1072
                     self.client.GetGroup("default"))
1073
    self.assertHandler(rlib2.R_2_groups_name)
1074
    self.assertItems(["default"])
1075

    
1076
  def testCreateGroup(self):
1077
    self.rapi.AddResponse("12345")
1078
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
1079
    self.assertEqual(job_id, 12345)
1080
    self.assertHandler(rlib2.R_2_groups)
1081
    self.assertDryRun()
1082

    
1083
  def testDeleteGroup(self):
1084
    self.rapi.AddResponse("12346")
1085
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1086
    self.assertEqual(job_id, 12346)
1087
    self.assertHandler(rlib2.R_2_groups_name)
1088
    self.assertDryRun()
1089

    
1090
  def testRenameGroup(self):
1091
    self.rapi.AddResponse("12347")
1092
    job_id = self.client.RenameGroup("oldname", "newname")
1093
    self.assertEqual(job_id, 12347)
1094
    self.assertHandler(rlib2.R_2_groups_name_rename)
1095

    
1096
  def testModifyGroup(self):
1097
    self.rapi.AddResponse("12348")
1098
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1099
    self.assertEqual(job_id, 12348)
1100
    self.assertHandler(rlib2.R_2_groups_name_modify)
1101

    
1102
  def testAssignGroupNodes(self):
1103
    self.rapi.AddResponse("12349")
1104
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1105
                                          force=True, dry_run=True)
1106
    self.assertEqual(job_id, 12349)
1107
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1108
    self.assertDryRun()
1109
    self.assertUseForce()
1110

    
1111
  def testModifyInstance(self):
1112
    self.rapi.AddResponse("23681")
1113
    job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1114
    self.assertEqual(job_id, 23681)
1115
    self.assertItems(["inst7210"])
1116
    self.assertHandler(rlib2.R_2_instances_name_modify)
1117
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1118
                     { "os_name": "linux", })
1119

    
1120
  def testModifyCluster(self):
1121
    for mnh in [None, False, True]:
1122
      self.rapi.AddResponse("14470")
1123
      self.assertEqual(14470,
1124
        self.client.ModifyCluster(maintain_node_health=mnh))
1125
      self.assertHandler(rlib2.R_2_cluster_modify)
1126
      self.assertItems([])
1127
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1128
      self.assertEqual(len(data), 1)
1129
      self.assertEqual(data["maintain_node_health"], mnh)
1130
      self.assertEqual(self.rapi.CountPending(), 0)
1131

    
1132
  def testRedistributeConfig(self):
1133
    self.rapi.AddResponse("3364")
1134
    job_id = self.client.RedistributeConfig()
1135
    self.assertEqual(job_id, 3364)
1136
    self.assertItems([])
1137
    self.assertHandler(rlib2.R_2_redist_config)
1138

    
1139
  def testActivateInstanceDisks(self):
1140
    self.rapi.AddResponse("23547")
1141
    job_id = self.client.ActivateInstanceDisks("inst28204")
1142
    self.assertEqual(job_id, 23547)
1143
    self.assertItems(["inst28204"])
1144
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1145
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1146

    
1147
  def testActivateInstanceDisksIgnoreSize(self):
1148
    self.rapi.AddResponse("11044")
1149
    job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1150
    self.assertEqual(job_id, 11044)
1151
    self.assertItems(["inst28204"])
1152
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1153
    self.assertQuery("ignore_size", ["1"])
1154

    
1155
  def testDeactivateInstanceDisks(self):
1156
    self.rapi.AddResponse("14591")
1157
    job_id = self.client.DeactivateInstanceDisks("inst28234")
1158
    self.assertEqual(job_id, 14591)
1159
    self.assertItems(["inst28234"])
1160
    self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1161
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1162

    
1163
  def testRecreateInstanceDisks(self):
1164
    self.rapi.AddResponse("13553")
1165
    job_id = self.client.RecreateInstanceDisks("inst23153")
1166
    self.assertEqual(job_id, 13553)
1167
    self.assertItems(["inst23153"])
1168
    self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1169
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1170

    
1171
  def testGetInstanceConsole(self):
1172
    self.rapi.AddResponse("26876")
1173
    job_id = self.client.GetInstanceConsole("inst21491")
1174
    self.assertEqual(job_id, 26876)
1175
    self.assertItems(["inst21491"])
1176
    self.assertHandler(rlib2.R_2_instances_name_console)
1177
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1178
    self.assertFalse(self.rapi.GetLastRequestData())
1179

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

    
1200
  def testGetGroupTags(self):
1201
    self.rapi.AddResponse("[]")
1202
    self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1203
    self.assertHandler(rlib2.R_2_groups_name_tags)
1204
    self.assertItems(["fooGroup"])
1205

    
1206
  def testAddGroupTags(self):
1207
    self.rapi.AddResponse("1234")
1208
    self.assertEqual(1234,
1209
        self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1210
    self.assertHandler(rlib2.R_2_groups_name_tags)
1211
    self.assertItems(["fooGroup"])
1212
    self.assertDryRun()
1213
    self.assertQuery("tag", ["awesome"])
1214

    
1215
  def testDeleteGroupTags(self):
1216
    self.rapi.AddResponse("25826")
1217
    self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1218
                                                        dry_run=True))
1219
    self.assertHandler(rlib2.R_2_groups_name_tags)
1220
    self.assertItems(["foo"])
1221
    self.assertDryRun()
1222
    self.assertQuery("tag", ["awesome"])
1223

    
1224
  def testQuery(self):
1225
    for idx, what in enumerate(constants.QR_VIA_RAPI):
1226
      for idx2, qfilter in enumerate([None, ["?", "name"]]):
1227
        job_id = 11010 + (idx << 4) + (idx2 << 16)
1228
        fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1229

    
1230
        self.rapi.AddResponse(str(job_id))
1231
        self.assertEqual(self.client.Query(what, fields, qfilter=qfilter),
1232
                         job_id)
1233
        self.assertItems([what])
1234
        self.assertHandler(rlib2.R_2_query)
1235
        self.assertFalse(self.rapi.GetLastHandler().queryargs)
1236
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
1237
        self.assertEqual(data["fields"], fields)
1238
        if qfilter is None:
1239
          self.assertTrue("qfilter" not in data)
1240
        else:
1241
          self.assertEqual(data["qfilter"], qfilter)
1242
        self.assertEqual(self.rapi.CountPending(), 0)
1243

    
1244
  def testQueryFields(self):
1245
    exp_result = objects.QueryFieldsResponse(fields=[
1246
      objects.QueryFieldDefinition(name="pnode", title="PNode",
1247
                                   kind=constants.QFT_NUMBER),
1248
      objects.QueryFieldDefinition(name="other", title="Other",
1249
                                   kind=constants.QFT_BOOL),
1250
      ])
1251

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

    
1260
        queryargs = self.rapi.GetLastHandler().queryargs
1261
        if fields is None:
1262
          self.assertFalse(queryargs)
1263
        else:
1264
          self.assertEqual(queryargs, {
1265
            "fields": [",".join(fields)],
1266
            })
1267

    
1268
        self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1269
                         exp_result.ToDict())
1270

    
1271
        self.assertEqual(self.rapi.CountPending(), 0)
1272

    
1273
  def testWaitForJobCompletionNoChange(self):
1274
    resp = serializer.DumpJson({
1275
      "status": constants.JOB_STATUS_WAITING,
1276
      })
1277

    
1278
    for retries in [1, 5, 25]:
1279
      for _ in range(retries):
1280
        self.rapi.AddResponse(resp)
1281

    
1282
      self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1283
                                                        retries=retries))
1284
      self.assertHandler(rlib2.R_2_jobs_id)
1285
      self.assertItems(["22789"])
1286

    
1287
      self.assertEqual(self.rapi.CountPending(), 0)
1288

    
1289
  def testWaitForJobCompletionAlreadyFinished(self):
1290
    self.rapi.AddResponse(serializer.DumpJson({
1291
      "status": constants.JOB_STATUS_SUCCESS,
1292
      }))
1293

    
1294
    self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1295
                                                     retries=1))
1296
    self.assertHandler(rlib2.R_2_jobs_id)
1297
    self.assertItems(["22793"])
1298

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

    
1301
  def testWaitForJobCompletionEmptyResponse(self):
1302
    self.rapi.AddResponse("{}")
1303
    self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1304
                                                     retries=10))
1305
    self.assertHandler(rlib2.R_2_jobs_id)
1306
    self.assertItems(["22793"])
1307

    
1308
    self.assertEqual(self.rapi.CountPending(), 0)
1309

    
1310
  def testWaitForJobCompletionOutOfRetries(self):
1311
    for retries in [3, 10, 21]:
1312
      for _ in range(retries):
1313
        self.rapi.AddResponse(serializer.DumpJson({
1314
          "status": constants.JOB_STATUS_RUNNING,
1315
          }))
1316

    
1317
      self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1318
                                                        retries=retries - 1))
1319
      self.assertHandler(rlib2.R_2_jobs_id)
1320
      self.assertItems(["30948"])
1321

    
1322
      self.assertEqual(self.rapi.CountPending(), 1)
1323
      self.rapi.ResetResponses()
1324

    
1325
  def testWaitForJobCompletionSuccessAndFailure(self):
1326
    for retries in [1, 4, 13]:
1327
      for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1328
                                    (True, constants.JOB_STATUS_SUCCESS)]:
1329
        for _ in range(retries):
1330
          self.rapi.AddResponse(serializer.DumpJson({
1331
            "status": constants.JOB_STATUS_RUNNING,
1332
            }))
1333

    
1334
        self.rapi.AddResponse(serializer.DumpJson({
1335
          "status": end_status,
1336
          }))
1337

    
1338
        result = self.client.WaitForJobCompletion(3187, period=None,
1339
                                                  retries=retries + 1)
1340
        self.assertEqual(result, success)
1341
        self.assertHandler(rlib2.R_2_jobs_id)
1342
        self.assertItems(["3187"])
1343

    
1344
        self.assertEqual(self.rapi.CountPending(), 0)
1345

    
1346

    
1347
class RapiTestRunner(unittest.TextTestRunner):
1348
  def run(self, *args):
1349
    global _used_handlers
1350
    assert _used_handlers is None
1351

    
1352
    _used_handlers = set()
1353
    try:
1354
      # Run actual tests
1355
      result = unittest.TextTestRunner.run(self, *args)
1356

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

    
1366
    return result
1367

    
1368

    
1369
if __name__ == '__main__':
1370
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)