Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 5823e0d2

History | View | Annotate | Download (31 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 CountPending(self):
98
    return len(self._responses)
99

    
100
  def GetLastHandler(self):
101
    return self._last_handler
102

    
103
  def GetLastRequestData(self):
104
    return self._last_req_data
105

    
106
  def FetchResponse(self, path, method, request_body):
107
    self._last_req_data = request_body
108

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

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

    
122
      (code, response) = self._responses.pop()
123

    
124
    return code, response
125

    
126

    
127
class TestConstants(unittest.TestCase):
128
  def test(self):
129
    self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
130
    self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
131
    self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
132
    self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
133
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
134
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
135

    
136

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

    
150

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

    
155

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

    
160

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

    
165

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

    
170

    
171
class TestExtendedConfig(unittest.TestCase):
172
  def testAuth(self):
173
    curl = FakeCurl(RapiMock())
174
    cl = client.GanetiRapiClient("master.example.com",
175
                                 username="user", password="pw",
176
                                 curl=curl)
177

    
178
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
179
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
180

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

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

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

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

    
212
            curl = FakeCurl(RapiMock())
213
            cl = client.GanetiRapiClient("master.example.com",
214
                                         curl_config_fn=cfgfn, curl=curl)
215

    
216
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
217
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
218

    
219
            if verify_hostname:
220
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
221
            else:
222
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
223

    
224
  def testNoCertVerify(self):
225
    cfgfn = client.GenericCurlConfig()
226

    
227
    curl = FakeCurl(RapiMock())
228
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
229
                                 curl=curl)
230

    
231
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
232
    self.assertFalse(curl.getopt(pycurl.CAINFO))
233
    self.assertFalse(curl.getopt(pycurl.CAPATH))
234

    
235
  def testCertVerifyCurlBundle(self):
236
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
237

    
238
    curl = FakeCurl(RapiMock())
239
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
240
                                 curl=curl)
241

    
242
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
243
    self.assertFalse(curl.getopt(pycurl.CAINFO))
244
    self.assertFalse(curl.getopt(pycurl.CAPATH))
245

    
246
  def testCertVerifyCafile(self):
247
    mycert = "/tmp/some/UNUSED/cert/file.pem"
248
    cfgfn = client.GenericCurlConfig(cafile=mycert)
249

    
250
    curl = FakeCurl(RapiMock())
251
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
252
                                 curl=curl)
253

    
254
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
255
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
256
    self.assertFalse(curl.getopt(pycurl.CAPATH))
257

    
258
  def testCertVerifyCapath(self):
259
    certdir = "/tmp/some/UNUSED/cert/directory"
260
    pcverfn = _FakeOpenSslPycurlVersion
261
    cfgfn = client.GenericCurlConfig(capath=certdir,
262
                                     _pycurl_version_fn=pcverfn)
263

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

    
268
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
269
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
270
    self.assertFalse(curl.getopt(pycurl.CAINFO))
271

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

    
278
    curl = FakeCurl(RapiMock())
279
    self.assertRaises(client.Error, client.GanetiRapiClient,
280
                      "master.example.com", curl_config_fn=cfgfn, curl=curl)
281

    
282
  def testCertVerifyNoSsl(self):
283
    certdir = "/tmp/some/UNUSED/cert/directory"
284
    pcverfn = _FakeNoSslPycurlVersion
285
    cfgfn = client.GenericCurlConfig(capath=certdir,
286
                                     _pycurl_version_fn=pcverfn)
287

    
288
    curl = FakeCurl(RapiMock())
289
    self.assertRaises(client.Error, client.GanetiRapiClient,
290
                      "master.example.com", curl_config_fn=cfgfn, curl=curl)
291

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

    
298
    curl = FakeCurl(RapiMock())
299
    self.assertRaises(NotImplementedError, client.GanetiRapiClient,
300
                      "master.example.com", curl_config_fn=cfgfn, curl=curl)
301

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

    
308
        curl = FakeCurl(RapiMock())
309
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
310
                                     curl=curl)
311

    
312
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
313
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
314

    
315

    
316
class GanetiRapiClientTests(testutils.GanetiTestCase):
317
  def setUp(self):
318
    testutils.GanetiTestCase.setUp(self)
319

    
320
    self.rapi = RapiMock()
321
    self.curl = FakeCurl(self.rapi)
322
    self.client = client.GanetiRapiClient("master.example.com",
323
                                          curl=self.curl)
324

    
325
    # Signals should be disabled by default
326
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
327

    
328
    # No auth and no proxy
329
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
330
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
331

    
332
    # Content-type is required for requests
333
    headers = self.curl.getopt(pycurl.HTTPHEADER)
334
    self.assert_("Content-type: application/json" in headers)
335

    
336
  def assertHandler(self, handler_cls):
337
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
338

    
339
  def assertQuery(self, key, value):
340
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
341

    
342
  def assertItems(self, items):
343
    self.assertEqual(items, self.rapi.GetLastHandler().items)
344

    
345
  def assertBulk(self):
346
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
347

    
348
  def assertDryRun(self):
349
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
350

    
351
  def testEncodeQuery(self):
352
    query = [
353
      ("a", None),
354
      ("b", 1),
355
      ("c", 2),
356
      ("d", "Foo"),
357
      ("e", True),
358
      ]
359

    
360
    expected = [
361
      ("a", ""),
362
      ("b", 1),
363
      ("c", 2),
364
      ("d", "Foo"),
365
      ("e", 1),
366
      ]
367

    
368
    self.assertEqualValues(self.client._EncodeQuery(query),
369
                           expected)
370

    
371
    # invalid types
372
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
373
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
374

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

    
384
  def testGetVersion(self):
385
    self.client._version = None
386
    self.rapi.AddResponse("2")
387
    self.assertEqual(2, self.client.GetVersion())
388
    self.assertHandler(rlib2.R_version)
389

    
390
  def testGetFeatures(self):
391
    for features in [[], ["foo", "bar", "baz"]]:
392
      self.rapi.AddResponse(serializer.DumpJson(features))
393
      self.assertEqual(features, self.client.GetFeatures())
394
      self.assertHandler(rlib2.R_2_features)
395

    
396
  def testGetFeaturesNotFound(self):
397
    self.rapi.AddResponse(None, code=404)
398
    self.assertEqual([], self.client.GetFeatures())
399

    
400
  def testGetOperatingSystems(self):
401
    self.rapi.AddResponse("[\"beos\"]")
402
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
403
    self.assertHandler(rlib2.R_2_os)
404

    
405
  def testGetClusterTags(self):
406
    self.rapi.AddResponse("[\"tag\"]")
407
    self.assertEqual(["tag"], self.client.GetClusterTags())
408
    self.assertHandler(rlib2.R_2_tags)
409

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

    
418
  def testDeleteClusterTags(self):
419
    self.rapi.AddResponse("5107")
420
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
421
                                                         dry_run=True))
422
    self.assertHandler(rlib2.R_2_tags)
423
    self.assertDryRun()
424
    self.assertQuery("tag", ["awesome"])
425

    
426
  def testGetInfo(self):
427
    self.rapi.AddResponse("{}")
428
    self.assertEqual({}, self.client.GetInfo())
429
    self.assertHandler(rlib2.R_2_info)
430

    
431
  def testGetInstances(self):
432
    self.rapi.AddResponse("[]")
433
    self.assertEqual([], self.client.GetInstances(bulk=True))
434
    self.assertHandler(rlib2.R_2_instances)
435
    self.assertBulk()
436

    
437
  def testGetInstance(self):
438
    self.rapi.AddResponse("[]")
439
    self.assertEqual([], self.client.GetInstance("instance"))
440
    self.assertHandler(rlib2.R_2_instances_name)
441
    self.assertItems(["instance"])
442

    
443
  def testGetInstanceInfo(self):
444
    self.rapi.AddResponse("21291")
445
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
446
    self.assertHandler(rlib2.R_2_instances_name_info)
447
    self.assertItems(["inst3"])
448
    self.assertQuery("static", None)
449

    
450
    self.rapi.AddResponse("3428")
451
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
452
    self.assertHandler(rlib2.R_2_instances_name_info)
453
    self.assertItems(["inst31"])
454
    self.assertQuery("static", ["0"])
455

    
456
    self.rapi.AddResponse("15665")
457
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
458
    self.assertHandler(rlib2.R_2_instances_name_info)
459
    self.assertItems(["inst32"])
460
    self.assertQuery("static", ["1"])
461

    
462
  def testCreateInstanceOldVersion(self):
463
    # No NICs
464
    self.rapi.AddResponse(None, code=404)
465
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
466
                      "create", "inst1.example.com", "plain", [], [])
467
    self.assertEqual(self.rapi.CountPending(), 0)
468

    
469
    # More than one NIC
470
    self.rapi.AddResponse(None, code=404)
471
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
472
                      "create", "inst1.example.com", "plain", [],
473
                      [{}, {}, {}])
474
    self.assertEqual(self.rapi.CountPending(), 0)
475

    
476
    # Unsupported NIC fields
477
    self.rapi.AddResponse(None, code=404)
478
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
479
                      "create", "inst1.example.com", "plain", [],
480
                      [{"x": True, "y": False}])
481
    self.assertEqual(self.rapi.CountPending(), 0)
482

    
483
    # Unsupported disk fields
484
    self.rapi.AddResponse(None, code=404)
485
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
486
                      "create", "inst1.example.com", "plain",
487
                      [{}, {"moo": "foo",}], [{}])
488
    self.assertEqual(self.rapi.CountPending(), 0)
489

    
490
    # Unsupported fields
491
    self.rapi.AddResponse(None, code=404)
492
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
493
                      "create", "inst1.example.com", "plain", [], [{}],
494
                      hello_world=123)
495
    self.assertEqual(self.rapi.CountPending(), 0)
496

    
497
    self.rapi.AddResponse(None, code=404)
498
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
499
                      "create", "inst1.example.com", "plain", [], [{}],
500
                      memory=128)
501
    self.assertEqual(self.rapi.CountPending(), 0)
502

    
503
    # Normal creation
504
    testnics = [
505
      [{}],
506
      [{ "mac": constants.VALUE_AUTO, }],
507
      [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
508
      ]
509

    
510
    testdisks = [
511
      [],
512
      [{ "size": 128, }],
513
      [{ "size": 321, }, { "size": 4096, }],
514
      ]
515

    
516
    for idx, nics in enumerate(testnics):
517
      for disks in testdisks:
518
        beparams = {
519
          constants.BE_MEMORY: 512,
520
          constants.BE_AUTO_BALANCE: False,
521
          }
522
        hvparams = {
523
          constants.HV_MIGRATION_PORT: 9876,
524
          constants.HV_VNC_TLS: True,
525
          }
526

    
527
        self.rapi.AddResponse(None, code=404)
528
        self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
529
        job_id = self.client.CreateInstance("create", "inst1.example.com",
530
                                            "plain", disks, nics,
531
                                            pnode="node99", dry_run=True,
532
                                            hvparams=hvparams,
533
                                            beparams=beparams)
534
        self.assertEqual(job_id, 3122617 + idx)
535
        self.assertHandler(rlib2.R_2_instances)
536
        self.assertDryRun()
537
        self.assertEqual(self.rapi.CountPending(), 0)
538

    
539
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
540
        self.assertEqual(data["name"], "inst1.example.com")
541
        self.assertEqual(data["disk_template"], "plain")
542
        self.assertEqual(data["pnode"], "node99")
543
        self.assertEqual(data[constants.BE_MEMORY], 512)
544
        self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
545
        self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
546
        self.assertEqual(data[constants.HV_VNC_TLS], True)
547
        self.assertEqual(data["disks"], [disk["size"] for disk in disks])
548

    
549
  def testCreateInstance(self):
550
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
551
    self.rapi.AddResponse("23030")
552
    job_id = self.client.CreateInstance("create", "inst1.example.com",
553
                                        "plain", [], [], dry_run=True)
554
    self.assertEqual(job_id, 23030)
555
    self.assertHandler(rlib2.R_2_instances)
556
    self.assertDryRun()
557

    
558
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
559

    
560
    for field in ["dry_run", "beparams", "hvparams", "start"]:
561
      self.assertFalse(field in data)
562

    
563
    self.assertEqual(data["name"], "inst1.example.com")
564
    self.assertEqual(data["disk_template"], "plain")
565

    
566
  def testCreateInstance2(self):
567
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
568
    self.rapi.AddResponse("24740")
569
    job_id = self.client.CreateInstance("import", "inst2.example.com",
570
                                        "drbd8", [{"size": 100,}],
571
                                        [{}, {"bridge": "br1", }],
572
                                        dry_run=False, start=True,
573
                                        pnode="node1", snode="node9",
574
                                        ip_check=False)
575
    self.assertEqual(job_id, 24740)
576
    self.assertHandler(rlib2.R_2_instances)
577

    
578
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
579
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
580
    self.assertEqual(data["name"], "inst2.example.com")
581
    self.assertEqual(data["disk_template"], "drbd8")
582
    self.assertEqual(data["start"], True)
583
    self.assertEqual(data["ip_check"], False)
584
    self.assertEqualValues(data["disks"], [{"size": 100,}])
585
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
586

    
587
  def testDeleteInstance(self):
588
    self.rapi.AddResponse("1234")
589
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
590
    self.assertHandler(rlib2.R_2_instances_name)
591
    self.assertItems(["instance"])
592
    self.assertDryRun()
593

    
594
  def testGetInstanceTags(self):
595
    self.rapi.AddResponse("[]")
596
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
597
    self.assertHandler(rlib2.R_2_instances_name_tags)
598
    self.assertItems(["fooinstance"])
599

    
600
  def testAddInstanceTags(self):
601
    self.rapi.AddResponse("1234")
602
    self.assertEqual(1234,
603
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
604
    self.assertHandler(rlib2.R_2_instances_name_tags)
605
    self.assertItems(["fooinstance"])
606
    self.assertDryRun()
607
    self.assertQuery("tag", ["awesome"])
608

    
609
  def testDeleteInstanceTags(self):
610
    self.rapi.AddResponse("25826")
611
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
612
                                                           dry_run=True))
613
    self.assertHandler(rlib2.R_2_instances_name_tags)
614
    self.assertItems(["foo"])
615
    self.assertDryRun()
616
    self.assertQuery("tag", ["awesome"])
617

    
618
  def testRebootInstance(self):
619
    self.rapi.AddResponse("6146")
620
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
621
                                        ignore_secondaries=True, dry_run=True)
622
    self.assertEqual(6146, job_id)
623
    self.assertHandler(rlib2.R_2_instances_name_reboot)
624
    self.assertItems(["i-bar"])
625
    self.assertDryRun()
626
    self.assertQuery("type", ["hard"])
627
    self.assertQuery("ignore_secondaries", ["1"])
628

    
629
  def testShutdownInstance(self):
630
    self.rapi.AddResponse("1487")
631
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
632
                                                        dry_run=True))
633
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
634
    self.assertItems(["foo-instance"])
635
    self.assertDryRun()
636

    
637
  def testStartupInstance(self):
638
    self.rapi.AddResponse("27149")
639
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
640
                                                        dry_run=True))
641
    self.assertHandler(rlib2.R_2_instances_name_startup)
642
    self.assertItems(["bar-instance"])
643
    self.assertDryRun()
644

    
645
  def testReinstallInstance(self):
646
    self.rapi.AddResponse("19119")
647
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
648
                                                          no_startup=True))
649
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
650
    self.assertItems(["baz-instance"])
651
    self.assertQuery("os", ["DOS"])
652
    self.assertQuery("nostartup", ["1"])
653

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

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

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

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

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

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

    
701
  def testGetJobs(self):
702
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
703
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
704
    self.assertEqual([123, 124], self.client.GetJobs())
705
    self.assertHandler(rlib2.R_2_jobs)
706

    
707
  def testGetJobStatus(self):
708
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
709
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
710
    self.assertHandler(rlib2.R_2_jobs_id)
711
    self.assertItems(["1234"])
712

    
713
  def testWaitForJobChange(self):
714
    fields = ["id", "summary"]
715
    expected = {
716
      "job_info": [123, "something"],
717
      "log_entries": [],
718
      }
719

    
720
    self.rapi.AddResponse(serializer.DumpJson(expected))
721
    result = self.client.WaitForJobChange(123, fields, [], -1)
722
    self.assertEqualValues(expected, result)
723
    self.assertHandler(rlib2.R_2_jobs_id_wait)
724
    self.assertItems(["123"])
725

    
726
  def testCancelJob(self):
727
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
728
    self.assertEqual([True, "Job 123 will be canceled"],
729
                     self.client.CancelJob(999, dry_run=True))
730
    self.assertHandler(rlib2.R_2_jobs_id)
731
    self.assertItems(["999"])
732
    self.assertDryRun()
733

    
734
  def testGetNodes(self):
735
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
736
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
737
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
738
    self.assertHandler(rlib2.R_2_nodes)
739

    
740
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
741
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
742
    self.assertEqual([{"id": "node1", "uri": "uri1"},
743
                      {"id": "node2", "uri": "uri2"}],
744
                     self.client.GetNodes(bulk=True))
745
    self.assertHandler(rlib2.R_2_nodes)
746
    self.assertBulk()
747

    
748
  def testGetNode(self):
749
    self.rapi.AddResponse("{}")
750
    self.assertEqual({}, self.client.GetNode("node-foo"))
751
    self.assertHandler(rlib2.R_2_nodes_name)
752
    self.assertItems(["node-foo"])
753

    
754
  def testEvacuateNode(self):
755
    self.rapi.AddResponse("9876")
756
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
757
    self.assertEqual(9876, job_id)
758
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
759
    self.assertItems(["node-1"])
760
    self.assertQuery("remote_node", ["node-2"])
761

    
762
    self.rapi.AddResponse("8888")
763
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
764
    self.assertEqual(8888, job_id)
765
    self.assertItems(["node-3"])
766
    self.assertQuery("iallocator", ["hail"])
767
    self.assertDryRun()
768

    
769
    self.assertRaises(client.GanetiApiError,
770
                      self.client.EvacuateNode,
771
                      "node-4", iallocator="hail", remote_node="node-5")
772

    
773
  def testMigrateNode(self):
774
    self.rapi.AddResponse("1111")
775
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
776
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
777
    self.assertItems(["node-a"])
778
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
779
    self.assertDryRun()
780

    
781
    self.rapi.AddResponse("1112")
782
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
783
                                                   mode="live"))
784
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
785
    self.assertItems(["node-a"])
786
    self.assertQuery("mode", ["live"])
787
    self.assertDryRun()
788

    
789
  def testGetNodeRole(self):
790
    self.rapi.AddResponse("\"master\"")
791
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
792
    self.assertHandler(rlib2.R_2_nodes_name_role)
793
    self.assertItems(["node-a"])
794

    
795
  def testSetNodeRole(self):
796
    self.rapi.AddResponse("789")
797
    self.assertEqual(789,
798
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
799
    self.assertHandler(rlib2.R_2_nodes_name_role)
800
    self.assertItems(["node-foo"])
801
    self.assertQuery("force", ["1"])
802
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
803

    
804
  def testGetNodeStorageUnits(self):
805
    self.rapi.AddResponse("42")
806
    self.assertEqual(42,
807
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
808
    self.assertHandler(rlib2.R_2_nodes_name_storage)
809
    self.assertItems(["node-x"])
810
    self.assertQuery("storage_type", ["lvm-pv"])
811
    self.assertQuery("output_fields", ["fields"])
812

    
813
  def testModifyNodeStorageUnits(self):
814
    self.rapi.AddResponse("14")
815
    self.assertEqual(14,
816
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
817
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
818
    self.assertItems(["node-z"])
819
    self.assertQuery("storage_type", ["lvm-pv"])
820
    self.assertQuery("name", ["hda"])
821
    self.assertQuery("allocatable", None)
822

    
823
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
824
      self.rapi.AddResponse("7205")
825
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
826
                                                  allocatable=allocatable)
827
      self.assertEqual(7205, job_id)
828
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
829
      self.assertItems(["node-z"])
830
      self.assertQuery("storage_type", ["lvm-pv"])
831
      self.assertQuery("name", ["hda"])
832
      self.assertQuery("allocatable", [query_allocatable])
833

    
834
  def testRepairNodeStorageUnits(self):
835
    self.rapi.AddResponse("99")
836
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
837
                                                            "hda"))
838
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
839
    self.assertItems(["node-z"])
840
    self.assertQuery("storage_type", ["lvm-pv"])
841
    self.assertQuery("name", ["hda"])
842

    
843
  def testGetNodeTags(self):
844
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
845
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
846
    self.assertHandler(rlib2.R_2_nodes_name_tags)
847
    self.assertItems(["node-k"])
848

    
849
  def testAddNodeTags(self):
850
    self.rapi.AddResponse("1234")
851
    self.assertEqual(1234,
852
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
853
    self.assertHandler(rlib2.R_2_nodes_name_tags)
854
    self.assertItems(["node-v"])
855
    self.assertDryRun()
856
    self.assertQuery("tag", ["awesome"])
857

    
858
  def testDeleteNodeTags(self):
859
    self.rapi.AddResponse("16861")
860
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
861
                                                       dry_run=True))
862
    self.assertHandler(rlib2.R_2_nodes_name_tags)
863
    self.assertItems(["node-w"])
864
    self.assertDryRun()
865
    self.assertQuery("tag", ["awesome"])
866

    
867

    
868
if __name__ == '__main__':
869
  client.UsesRapiClient(testutils.GanetiTestProgram)()