Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 2a7c3583

History | View | Annotate | Download (27.2 kB)

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

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

    
21

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

    
24

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

    
30
from ganeti import constants
31
from ganeti import http
32
from ganeti import serializer
33

    
34
from ganeti.rapi import connector
35
from ganeti.rapi import rlib2
36
from ganeti.rapi import client
37

    
38
import testutils
39

    
40

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

    
43

    
44
def _GetPathFromUri(uri):
45
  """Gets the path and query from a URI.
46

47
  """
48
  match = _URI_RE.match(uri)
49
  if match:
50
    return match.groupdict()["path"]
51
  else:
52
    return None
53

    
54

    
55
class FakeCurl:
56
  def __init__(self, rapi):
57
    self._rapi = rapi
58
    self._opts = {}
59
    self._info = {}
60

    
61
  def setopt(self, opt, value):
62
    self._opts[opt] = value
63

    
64
  def getopt(self, opt):
65
    return self._opts.get(opt)
66

    
67
  def unsetopt(self, opt):
68
    self._opts.pop(opt, None)
69

    
70
  def getinfo(self, info):
71
    return self._info[info]
72

    
73
  def perform(self):
74
    method = self._opts[pycurl.CUSTOMREQUEST]
75
    url = self._opts[pycurl.URL]
76
    request_body = self._opts[pycurl.POSTFIELDS]
77
    writefn = self._opts[pycurl.WRITEFUNCTION]
78

    
79
    path = _GetPathFromUri(url)
80
    (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
81

    
82
    self._info[pycurl.RESPONSE_CODE] = code
83
    if resp_body is not None:
84
      writefn(resp_body)
85

    
86

    
87
class RapiMock(object):
88
  def __init__(self):
89
    self._mapper = connector.Mapper()
90
    self._responses = []
91
    self._last_handler = None
92
    self._last_req_data = None
93

    
94
  def AddResponse(self, response, code=200):
95
    self._responses.insert(0, (code, response))
96

    
97
  def GetLastHandler(self):
98
    return self._last_handler
99

    
100
  def GetLastRequestData(self):
101
    return self._last_req_data
102

    
103
  def FetchResponse(self, path, method, request_body):
104
    self._last_req_data = request_body
105

    
106
    try:
107
      HandlerClass, items, args = self._mapper.getController(path)
108
      self._last_handler = HandlerClass(items, args, None)
109
      if not hasattr(self._last_handler, method.upper()):
110
        raise http.HttpNotImplemented(message="Method not implemented")
111

    
112
    except http.HttpException, ex:
113
      code = ex.code
114
      response = ex.message
115
    else:
116
      if not self._responses:
117
        raise Exception("No responses")
118

    
119
      (code, response) = self._responses.pop()
120

    
121
    return code, response
122

    
123

    
124
class TestConstants(unittest.TestCase):
125
  def test(self):
126
    self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
127
    self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
128
    self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
129
    self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
130
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
131

    
132

    
133
class RapiMockTest(unittest.TestCase):
134
  def test(self):
135
    rapi = RapiMock()
136
    path = "/version"
137
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
138
    self.assertEqual((501, "Method not implemented"),
139
                     rapi.FetchResponse("/version", "POST", None))
140
    rapi.AddResponse("2")
141
    code, response = rapi.FetchResponse("/version", "GET", None)
142
    self.assertEqual(200, code)
143
    self.assertEqual("2", response)
144
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
145

    
146

    
147
def _FakeNoSslPycurlVersion():
148
  # Note: incomplete version tuple
149
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
150

    
151

    
152
def _FakeFancySslPycurlVersion():
153
  # Note: incomplete version tuple
154
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
155

    
156

    
157
def _FakeOpenSslPycurlVersion():
158
  # Note: incomplete version tuple
159
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
160

    
161

    
162
def _FakeGnuTlsPycurlVersion():
163
  # Note: incomplete version tuple
164
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
165

    
166

    
167
class TestExtendedConfig(unittest.TestCase):
168
  def testAuth(self):
169
    curl = FakeCurl(RapiMock())
170
    cl = client.GanetiRapiClient("master.example.com",
171
                                 username="user", password="pw",
172
                                 curl=curl)
173

    
174
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
175
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
176

    
177
  def testInvalidAuth(self):
178
    # No username
179
    self.assertRaises(client.Error, client.GanetiRapiClient,
180
                      "master-a.example.com", password="pw")
181
    # No password
182
    self.assertRaises(client.Error, client.GanetiRapiClient,
183
                      "master-b.example.com", username="user")
184

    
185
  def testCertVerifyInvalidCombinations(self):
186
    self.assertRaises(client.Error, client.GenericCurlConfig,
187
                      use_curl_cabundle=True, cafile="cert1.pem")
188
    self.assertRaises(client.Error, client.GenericCurlConfig,
189
                      use_curl_cabundle=True, capath="certs/")
190
    self.assertRaises(client.Error, client.GenericCurlConfig,
191
                      use_curl_cabundle=True,
192
                      cafile="cert1.pem", capath="certs/")
193

    
194
  def testProxySignalVerifyHostname(self):
195
    for use_gnutls in [False, True]:
196
      if use_gnutls:
197
        pcverfn = _FakeGnuTlsPycurlVersion
198
      else:
199
        pcverfn = _FakeOpenSslPycurlVersion
200

    
201
      for proxy in ["", "http://127.0.0.1:1234"]:
202
        for use_signal in [False, True]:
203
          for verify_hostname in [False, True]:
204
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
205
                                             verify_hostname=verify_hostname,
206
                                             _pycurl_version_fn=pcverfn)
207

    
208
            curl = FakeCurl(RapiMock())
209
            cl = client.GanetiRapiClient("master.example.com",
210
                                         curl_config_fn=cfgfn, curl=curl)
211

    
212
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
213
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
214

    
215
            if verify_hostname:
216
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
217
            else:
218
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
219

    
220
  def testNoCertVerify(self):
221
    cfgfn = client.GenericCurlConfig()
222

    
223
    curl = FakeCurl(RapiMock())
224
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
225
                                 curl=curl)
226

    
227
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
228
    self.assertFalse(curl.getopt(pycurl.CAINFO))
229
    self.assertFalse(curl.getopt(pycurl.CAPATH))
230

    
231
  def testCertVerifyCurlBundle(self):
232
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
233

    
234
    curl = FakeCurl(RapiMock())
235
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
236
                                 curl=curl)
237

    
238
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
239
    self.assertFalse(curl.getopt(pycurl.CAINFO))
240
    self.assertFalse(curl.getopt(pycurl.CAPATH))
241

    
242
  def testCertVerifyCafile(self):
243
    mycert = "/tmp/some/UNUSED/cert/file.pem"
244
    cfgfn = client.GenericCurlConfig(cafile=mycert)
245

    
246
    curl = FakeCurl(RapiMock())
247
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
248
                                 curl=curl)
249

    
250
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
251
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
252
    self.assertFalse(curl.getopt(pycurl.CAPATH))
253

    
254
  def testCertVerifyCapath(self):
255
    certdir = "/tmp/some/UNUSED/cert/directory"
256
    pcverfn = _FakeOpenSslPycurlVersion
257
    cfgfn = client.GenericCurlConfig(capath=certdir,
258
                                     _pycurl_version_fn=pcverfn)
259

    
260
    curl = FakeCurl(RapiMock())
261
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
262
                                 curl=curl)
263

    
264
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
265
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
266
    self.assertFalse(curl.getopt(pycurl.CAINFO))
267

    
268
  def testCertVerifyCapathGnuTls(self):
269
    certdir = "/tmp/some/UNUSED/cert/directory"
270
    pcverfn = _FakeGnuTlsPycurlVersion
271
    cfgfn = client.GenericCurlConfig(capath=certdir,
272
                                     _pycurl_version_fn=pcverfn)
273

    
274
    curl = FakeCurl(RapiMock())
275
    self.assertRaises(client.Error, client.GanetiRapiClient,
276
                      "master.example.com", curl_config_fn=cfgfn, curl=curl)
277

    
278
  def testCertVerifyNoSsl(self):
279
    certdir = "/tmp/some/UNUSED/cert/directory"
280
    pcverfn = _FakeNoSslPycurlVersion
281
    cfgfn = client.GenericCurlConfig(capath=certdir,
282
                                     _pycurl_version_fn=pcverfn)
283

    
284
    curl = FakeCurl(RapiMock())
285
    self.assertRaises(client.Error, client.GanetiRapiClient,
286
                      "master.example.com", curl_config_fn=cfgfn, curl=curl)
287

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

    
294
    curl = FakeCurl(RapiMock())
295
    self.assertRaises(NotImplementedError, client.GanetiRapiClient,
296
                      "master.example.com", curl_config_fn=cfgfn, curl=curl)
297

    
298
  def testCertVerifyCapath(self):
299
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
300
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
301
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
302
                                         timeout=timeout)
303

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

    
308
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
309
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
310

    
311

    
312
class GanetiRapiClientTests(testutils.GanetiTestCase):
313
  def setUp(self):
314
    testutils.GanetiTestCase.setUp(self)
315

    
316
    self.rapi = RapiMock()
317
    self.curl = FakeCurl(self.rapi)
318
    self.client = client.GanetiRapiClient("master.example.com",
319
                                          curl=self.curl)
320

    
321
    # Signals should be disabled by default
322
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
323

    
324
    # No auth and no proxy
325
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
326
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
327

    
328
    # Content-type is required for requests
329
    headers = self.curl.getopt(pycurl.HTTPHEADER)
330
    self.assert_("Content-type: application/json" in headers)
331

    
332
  def assertHandler(self, handler_cls):
333
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
334

    
335
  def assertQuery(self, key, value):
336
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
337

    
338
  def assertItems(self, items):
339
    self.assertEqual(items, self.rapi.GetLastHandler().items)
340

    
341
  def assertBulk(self):
342
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
343

    
344
  def assertDryRun(self):
345
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
346

    
347
  def testEncodeQuery(self):
348
    query = [
349
      ("a", None),
350
      ("b", 1),
351
      ("c", 2),
352
      ("d", "Foo"),
353
      ("e", True),
354
      ]
355

    
356
    expected = [
357
      ("a", ""),
358
      ("b", 1),
359
      ("c", 2),
360
      ("d", "Foo"),
361
      ("e", 1),
362
      ]
363

    
364
    self.assertEqualValues(self.client._EncodeQuery(query),
365
                           expected)
366

    
367
    # invalid types
368
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
369
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
370

    
371
  def testHttpError(self):
372
    self.rapi.AddResponse(None, code=404)
373
    try:
374
      self.client.GetJobStatus(15140)
375
    except client.GanetiApiError, err:
376
      self.assertEqual(err.code, 404)
377
    else:
378
      self.fail("Didn't raise exception")
379

    
380
  def testGetVersion(self):
381
    self.client._version = None
382
    self.rapi.AddResponse("2")
383
    self.assertEqual(2, self.client.GetVersion())
384
    self.assertHandler(rlib2.R_version)
385

    
386
  def testGetFeatures(self):
387
    for features in [[], ["foo", "bar", "baz"]]:
388
      self.rapi.AddResponse(serializer.DumpJson(features))
389
      self.assertEqual(features, self.client.GetFeatures())
390
      self.assertHandler(rlib2.R_2_features)
391

    
392
  def testGetOperatingSystems(self):
393
    self.rapi.AddResponse("[\"beos\"]")
394
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
395
    self.assertHandler(rlib2.R_2_os)
396

    
397
  def testGetClusterTags(self):
398
    self.rapi.AddResponse("[\"tag\"]")
399
    self.assertEqual(["tag"], self.client.GetClusterTags())
400
    self.assertHandler(rlib2.R_2_tags)
401

    
402
  def testAddClusterTags(self):
403
    self.rapi.AddResponse("1234")
404
    self.assertEqual(1234,
405
        self.client.AddClusterTags(["awesome"], dry_run=True))
406
    self.assertHandler(rlib2.R_2_tags)
407
    self.assertDryRun()
408
    self.assertQuery("tag", ["awesome"])
409

    
410
  def testDeleteClusterTags(self):
411
    self.rapi.AddResponse("5107")
412
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
413
                                                         dry_run=True))
414
    self.assertHandler(rlib2.R_2_tags)
415
    self.assertDryRun()
416
    self.assertQuery("tag", ["awesome"])
417

    
418
  def testGetInfo(self):
419
    self.rapi.AddResponse("{}")
420
    self.assertEqual({}, self.client.GetInfo())
421
    self.assertHandler(rlib2.R_2_info)
422

    
423
  def testGetInstances(self):
424
    self.rapi.AddResponse("[]")
425
    self.assertEqual([], self.client.GetInstances(bulk=True))
426
    self.assertHandler(rlib2.R_2_instances)
427
    self.assertBulk()
428

    
429
  def testGetInstance(self):
430
    self.rapi.AddResponse("[]")
431
    self.assertEqual([], self.client.GetInstance("instance"))
432
    self.assertHandler(rlib2.R_2_instances_name)
433
    self.assertItems(["instance"])
434

    
435
  def testGetInstanceInfo(self):
436
    self.rapi.AddResponse("21291")
437
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
438
    self.assertHandler(rlib2.R_2_instances_name_info)
439
    self.assertItems(["inst3"])
440
    self.assertQuery("static", None)
441

    
442
    self.rapi.AddResponse("3428")
443
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
444
    self.assertHandler(rlib2.R_2_instances_name_info)
445
    self.assertItems(["inst31"])
446
    self.assertQuery("static", ["0"])
447

    
448
    self.rapi.AddResponse("15665")
449
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
450
    self.assertHandler(rlib2.R_2_instances_name_info)
451
    self.assertItems(["inst32"])
452
    self.assertQuery("static", ["1"])
453

    
454
  def testCreateInstanceOldVersion(self):
455
    self.rapi.AddResponse(serializer.DumpJson([]))
456
    self.assertRaises(NotImplementedError, self.client.CreateInstance,
457
                      "create", "inst1.example.com", "plain", [], [],
458
                      dry_run=True)
459

    
460
  def testCreateInstance(self):
461
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
462
    self.rapi.AddResponse("23030")
463
    job_id = self.client.CreateInstance("create", "inst1.example.com",
464
                                        "plain", [], [], dry_run=True)
465
    self.assertEqual(job_id, 23030)
466
    self.assertHandler(rlib2.R_2_instances)
467
    self.assertDryRun()
468

    
469
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
470

    
471
    for field in ["dry_run", "beparams", "hvparams", "start"]:
472
      self.assertFalse(field in data)
473

    
474
    self.assertEqual(data["name"], "inst1.example.com")
475
    self.assertEqual(data["disk_template"], "plain")
476

    
477
  def testCreateInstance2(self):
478
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
479
    self.rapi.AddResponse("24740")
480
    job_id = self.client.CreateInstance("import", "inst2.example.com",
481
                                        "drbd8", [{"size": 100,}],
482
                                        [{}, {"bridge": "br1", }],
483
                                        dry_run=False, start=True,
484
                                        pnode="node1", snode="node9",
485
                                        ip_check=False)
486
    self.assertEqual(job_id, 24740)
487
    self.assertHandler(rlib2.R_2_instances)
488

    
489
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
490
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
491
    self.assertEqual(data["name"], "inst2.example.com")
492
    self.assertEqual(data["disk_template"], "drbd8")
493
    self.assertEqual(data["start"], True)
494
    self.assertEqual(data["ip_check"], False)
495
    self.assertEqualValues(data["disks"], [{"size": 100,}])
496
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
497

    
498
  def testDeleteInstance(self):
499
    self.rapi.AddResponse("1234")
500
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
501
    self.assertHandler(rlib2.R_2_instances_name)
502
    self.assertItems(["instance"])
503
    self.assertDryRun()
504

    
505
  def testGetInstanceTags(self):
506
    self.rapi.AddResponse("[]")
507
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
508
    self.assertHandler(rlib2.R_2_instances_name_tags)
509
    self.assertItems(["fooinstance"])
510

    
511
  def testAddInstanceTags(self):
512
    self.rapi.AddResponse("1234")
513
    self.assertEqual(1234,
514
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
515
    self.assertHandler(rlib2.R_2_instances_name_tags)
516
    self.assertItems(["fooinstance"])
517
    self.assertDryRun()
518
    self.assertQuery("tag", ["awesome"])
519

    
520
  def testDeleteInstanceTags(self):
521
    self.rapi.AddResponse("25826")
522
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
523
                                                           dry_run=True))
524
    self.assertHandler(rlib2.R_2_instances_name_tags)
525
    self.assertItems(["foo"])
526
    self.assertDryRun()
527
    self.assertQuery("tag", ["awesome"])
528

    
529
  def testRebootInstance(self):
530
    self.rapi.AddResponse("6146")
531
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
532
                                        ignore_secondaries=True, dry_run=True)
533
    self.assertEqual(6146, job_id)
534
    self.assertHandler(rlib2.R_2_instances_name_reboot)
535
    self.assertItems(["i-bar"])
536
    self.assertDryRun()
537
    self.assertQuery("type", ["hard"])
538
    self.assertQuery("ignore_secondaries", ["1"])
539

    
540
  def testShutdownInstance(self):
541
    self.rapi.AddResponse("1487")
542
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
543
                                                        dry_run=True))
544
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
545
    self.assertItems(["foo-instance"])
546
    self.assertDryRun()
547

    
548
  def testStartupInstance(self):
549
    self.rapi.AddResponse("27149")
550
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
551
                                                        dry_run=True))
552
    self.assertHandler(rlib2.R_2_instances_name_startup)
553
    self.assertItems(["bar-instance"])
554
    self.assertDryRun()
555

    
556
  def testReinstallInstance(self):
557
    self.rapi.AddResponse("19119")
558
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
559
                                                          no_startup=True))
560
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
561
    self.assertItems(["baz-instance"])
562
    self.assertQuery("os", ["DOS"])
563
    self.assertQuery("nostartup", ["1"])
564

    
565
  def testReplaceInstanceDisks(self):
566
    self.rapi.AddResponse("999")
567
    job_id = self.client.ReplaceInstanceDisks("instance-name",
568
        disks=[0, 1], dry_run=True, iallocator="hail")
569
    self.assertEqual(999, job_id)
570
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
571
    self.assertItems(["instance-name"])
572
    self.assertQuery("disks", ["0,1"])
573
    self.assertQuery("mode", ["replace_auto"])
574
    self.assertQuery("iallocator", ["hail"])
575
    self.assertDryRun()
576

    
577
    self.rapi.AddResponse("1000")
578
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
579
        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
580
        dry_run=True)
581
    self.assertEqual(1000, job_id)
582
    self.assertItems(["instance-bar"])
583
    self.assertQuery("disks", ["1"])
584
    self.assertQuery("remote_node", ["foo-node"])
585
    self.assertDryRun()
586

    
587
    self.rapi.AddResponse("5175")
588
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
589
    self.assertItems(["instance-moo"])
590
    self.assertQuery("disks", None)
591

    
592
  def testPrepareExport(self):
593
    self.rapi.AddResponse("8326")
594
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
595
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
596
    self.assertItems(["inst1"])
597
    self.assertQuery("mode", ["local"])
598

    
599
  def testExportInstance(self):
600
    self.rapi.AddResponse("19695")
601
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
602
                                        shutdown=True)
603
    self.assertEqual(job_id, 19695)
604
    self.assertHandler(rlib2.R_2_instances_name_export)
605
    self.assertItems(["inst2"])
606

    
607
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
608
    self.assertEqual(data["mode"], "local")
609
    self.assertEqual(data["destination"], "nodeX")
610
    self.assertEqual(data["shutdown"], True)
611

    
612
  def testGetJobs(self):
613
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
614
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
615
    self.assertEqual([123, 124], self.client.GetJobs())
616
    self.assertHandler(rlib2.R_2_jobs)
617

    
618
  def testGetJobStatus(self):
619
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
620
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
621
    self.assertHandler(rlib2.R_2_jobs_id)
622
    self.assertItems(["1234"])
623

    
624
  def testWaitForJobChange(self):
625
    fields = ["id", "summary"]
626
    expected = {
627
      "job_info": [123, "something"],
628
      "log_entries": [],
629
      }
630

    
631
    self.rapi.AddResponse(serializer.DumpJson(expected))
632
    result = self.client.WaitForJobChange(123, fields, [], -1)
633
    self.assertEqualValues(expected, result)
634
    self.assertHandler(rlib2.R_2_jobs_id_wait)
635
    self.assertItems(["123"])
636

    
637
  def testCancelJob(self):
638
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
639
    self.assertEqual([True, "Job 123 will be canceled"],
640
                     self.client.CancelJob(999, dry_run=True))
641
    self.assertHandler(rlib2.R_2_jobs_id)
642
    self.assertItems(["999"])
643
    self.assertDryRun()
644

    
645
  def testGetNodes(self):
646
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
647
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
648
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
649
    self.assertHandler(rlib2.R_2_nodes)
650

    
651
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
652
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
653
    self.assertEqual([{"id": "node1", "uri": "uri1"},
654
                      {"id": "node2", "uri": "uri2"}],
655
                     self.client.GetNodes(bulk=True))
656
    self.assertHandler(rlib2.R_2_nodes)
657
    self.assertBulk()
658

    
659
  def testGetNode(self):
660
    self.rapi.AddResponse("{}")
661
    self.assertEqual({}, self.client.GetNode("node-foo"))
662
    self.assertHandler(rlib2.R_2_nodes_name)
663
    self.assertItems(["node-foo"])
664

    
665
  def testEvacuateNode(self):
666
    self.rapi.AddResponse("9876")
667
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
668
    self.assertEqual(9876, job_id)
669
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
670
    self.assertItems(["node-1"])
671
    self.assertQuery("remote_node", ["node-2"])
672

    
673
    self.rapi.AddResponse("8888")
674
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
675
    self.assertEqual(8888, job_id)
676
    self.assertItems(["node-3"])
677
    self.assertQuery("iallocator", ["hail"])
678
    self.assertDryRun()
679

    
680
    self.assertRaises(client.GanetiApiError,
681
                      self.client.EvacuateNode,
682
                      "node-4", iallocator="hail", remote_node="node-5")
683

    
684
  def testMigrateNode(self):
685
    self.rapi.AddResponse("1111")
686
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
687
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
688
    self.assertItems(["node-a"])
689
    self.assertQuery("live", ["1"])
690
    self.assertDryRun()
691

    
692
  def testGetNodeRole(self):
693
    self.rapi.AddResponse("\"master\"")
694
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
695
    self.assertHandler(rlib2.R_2_nodes_name_role)
696
    self.assertItems(["node-a"])
697

    
698
  def testSetNodeRole(self):
699
    self.rapi.AddResponse("789")
700
    self.assertEqual(789,
701
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
702
    self.assertHandler(rlib2.R_2_nodes_name_role)
703
    self.assertItems(["node-foo"])
704
    self.assertQuery("force", ["1"])
705
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
706

    
707
  def testGetNodeStorageUnits(self):
708
    self.rapi.AddResponse("42")
709
    self.assertEqual(42,
710
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
711
    self.assertHandler(rlib2.R_2_nodes_name_storage)
712
    self.assertItems(["node-x"])
713
    self.assertQuery("storage_type", ["lvm-pv"])
714
    self.assertQuery("output_fields", ["fields"])
715

    
716
  def testModifyNodeStorageUnits(self):
717
    self.rapi.AddResponse("14")
718
    self.assertEqual(14,
719
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
720
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
721
    self.assertItems(["node-z"])
722
    self.assertQuery("storage_type", ["lvm-pv"])
723
    self.assertQuery("name", ["hda"])
724
    self.assertQuery("allocatable", None)
725

    
726
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
727
      self.rapi.AddResponse("7205")
728
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
729
                                                  allocatable=allocatable)
730
      self.assertEqual(7205, job_id)
731
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
732
      self.assertItems(["node-z"])
733
      self.assertQuery("storage_type", ["lvm-pv"])
734
      self.assertQuery("name", ["hda"])
735
      self.assertQuery("allocatable", [query_allocatable])
736

    
737
  def testRepairNodeStorageUnits(self):
738
    self.rapi.AddResponse("99")
739
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
740
                                                            "hda"))
741
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
742
    self.assertItems(["node-z"])
743
    self.assertQuery("storage_type", ["lvm-pv"])
744
    self.assertQuery("name", ["hda"])
745

    
746
  def testGetNodeTags(self):
747
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
748
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
749
    self.assertHandler(rlib2.R_2_nodes_name_tags)
750
    self.assertItems(["node-k"])
751

    
752
  def testAddNodeTags(self):
753
    self.rapi.AddResponse("1234")
754
    self.assertEqual(1234,
755
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
756
    self.assertHandler(rlib2.R_2_nodes_name_tags)
757
    self.assertItems(["node-v"])
758
    self.assertDryRun()
759
    self.assertQuery("tag", ["awesome"])
760

    
761
  def testDeleteNodeTags(self):
762
    self.rapi.AddResponse("16861")
763
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
764
                                                       dry_run=True))
765
    self.assertHandler(rlib2.R_2_nodes_name_tags)
766
    self.assertItems(["node-w"])
767
    self.assertDryRun()
768
    self.assertQuery("tag", ["awesome"])
769

    
770

    
771
if __name__ == '__main__':
772
  client.UsesRapiClient(testutils.GanetiTestProgram)()