Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (35.8 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_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
135
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
136

    
137

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

    
151

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

    
156

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

    
161

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

    
166

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

    
171

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
329

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

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

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

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

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

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

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

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

    
363
    expected = [
364
      ("a", ""),
365
      ("b", 1),
366
      ("c", 2),
367
      ("d", "Foo"),
368
      ("e", 1),
369
      ]
370

    
371
    self.assertEqualValues(self.client._EncodeQuery(query),
372
                           expected)
373

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

    
378
  def testCurlSettings(self):
379
    self.rapi.AddResponse("2")
380
    self.assertEqual(2, self.client.GetVersion())
381
    self.assertHandler(rlib2.R_version)
382

    
383
    # Signals should be disabled by default
384
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
385

    
386
    # No auth and no proxy
387
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
388
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
389

    
390
    # Content-type is required for requests
391
    headers = self.curl.getopt(pycurl.HTTPHEADER)
392
    self.assert_("Content-type: application/json" in headers)
393

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

    
403
  def testGetVersion(self):
404
    self.rapi.AddResponse("2")
405
    self.assertEqual(2, self.client.GetVersion())
406
    self.assertHandler(rlib2.R_version)
407

    
408
  def testGetFeatures(self):
409
    for features in [[], ["foo", "bar", "baz"]]:
410
      self.rapi.AddResponse(serializer.DumpJson(features))
411
      self.assertEqual(features, self.client.GetFeatures())
412
      self.assertHandler(rlib2.R_2_features)
413

    
414
  def testGetFeaturesNotFound(self):
415
    self.rapi.AddResponse(None, code=404)
416
    self.assertEqual([], self.client.GetFeatures())
417

    
418
  def testGetOperatingSystems(self):
419
    self.rapi.AddResponse("[\"beos\"]")
420
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
421
    self.assertHandler(rlib2.R_2_os)
422

    
423
  def testGetClusterTags(self):
424
    self.rapi.AddResponse("[\"tag\"]")
425
    self.assertEqual(["tag"], self.client.GetClusterTags())
426
    self.assertHandler(rlib2.R_2_tags)
427

    
428
  def testAddClusterTags(self):
429
    self.rapi.AddResponse("1234")
430
    self.assertEqual(1234,
431
        self.client.AddClusterTags(["awesome"], dry_run=True))
432
    self.assertHandler(rlib2.R_2_tags)
433
    self.assertDryRun()
434
    self.assertQuery("tag", ["awesome"])
435

    
436
  def testDeleteClusterTags(self):
437
    self.rapi.AddResponse("5107")
438
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
439
                                                         dry_run=True))
440
    self.assertHandler(rlib2.R_2_tags)
441
    self.assertDryRun()
442
    self.assertQuery("tag", ["awesome"])
443

    
444
  def testGetInfo(self):
445
    self.rapi.AddResponse("{}")
446
    self.assertEqual({}, self.client.GetInfo())
447
    self.assertHandler(rlib2.R_2_info)
448

    
449
  def testGetInstances(self):
450
    self.rapi.AddResponse("[]")
451
    self.assertEqual([], self.client.GetInstances(bulk=True))
452
    self.assertHandler(rlib2.R_2_instances)
453
    self.assertBulk()
454

    
455
  def testGetInstance(self):
456
    self.rapi.AddResponse("[]")
457
    self.assertEqual([], self.client.GetInstance("instance"))
458
    self.assertHandler(rlib2.R_2_instances_name)
459
    self.assertItems(["instance"])
460

    
461
  def testGetInstanceInfo(self):
462
    self.rapi.AddResponse("21291")
463
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
464
    self.assertHandler(rlib2.R_2_instances_name_info)
465
    self.assertItems(["inst3"])
466
    self.assertQuery("static", None)
467

    
468
    self.rapi.AddResponse("3428")
469
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
470
    self.assertHandler(rlib2.R_2_instances_name_info)
471
    self.assertItems(["inst31"])
472
    self.assertQuery("static", ["0"])
473

    
474
    self.rapi.AddResponse("15665")
475
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
476
    self.assertHandler(rlib2.R_2_instances_name_info)
477
    self.assertItems(["inst32"])
478
    self.assertQuery("static", ["1"])
479

    
480
  def testCreateInstanceOldVersion(self):
481
    # No NICs
482
    self.rapi.AddResponse(None, code=404)
483
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
484
                      "create", "inst1.example.com", "plain", [], [])
485
    self.assertEqual(self.rapi.CountPending(), 0)
486

    
487
    # More than one NIC
488
    self.rapi.AddResponse(None, code=404)
489
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
490
                      "create", "inst1.example.com", "plain", [],
491
                      [{}, {}, {}])
492
    self.assertEqual(self.rapi.CountPending(), 0)
493

    
494
    # Unsupported NIC fields
495
    self.rapi.AddResponse(None, code=404)
496
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
497
                      "create", "inst1.example.com", "plain", [],
498
                      [{"x": True, "y": False}])
499
    self.assertEqual(self.rapi.CountPending(), 0)
500

    
501
    # Unsupported disk fields
502
    self.rapi.AddResponse(None, code=404)
503
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
504
                      "create", "inst1.example.com", "plain",
505
                      [{}, {"moo": "foo",}], [{}])
506
    self.assertEqual(self.rapi.CountPending(), 0)
507

    
508
    # Unsupported fields
509
    self.rapi.AddResponse(None, code=404)
510
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
511
                      "create", "inst1.example.com", "plain", [], [{}],
512
                      hello_world=123)
513
    self.assertEqual(self.rapi.CountPending(), 0)
514

    
515
    self.rapi.AddResponse(None, code=404)
516
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
517
                      "create", "inst1.example.com", "plain", [], [{}],
518
                      memory=128)
519
    self.assertEqual(self.rapi.CountPending(), 0)
520

    
521
    # Normal creation
522
    testnics = [
523
      [{}],
524
      [{ "mac": constants.VALUE_AUTO, }],
525
      [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
526
      ]
527

    
528
    testdisks = [
529
      [],
530
      [{ "size": 128, }],
531
      [{ "size": 321, }, { "size": 4096, }],
532
      ]
533

    
534
    for idx, nics in enumerate(testnics):
535
      for disks in testdisks:
536
        beparams = {
537
          constants.BE_MEMORY: 512,
538
          constants.BE_AUTO_BALANCE: False,
539
          }
540
        hvparams = {
541
          constants.HV_MIGRATION_PORT: 9876,
542
          constants.HV_VNC_TLS: True,
543
          }
544

    
545
        self.rapi.AddResponse(None, code=404)
546
        self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
547
        job_id = self.client.CreateInstance("create", "inst1.example.com",
548
                                            "plain", disks, nics,
549
                                            pnode="node99", dry_run=True,
550
                                            hvparams=hvparams,
551
                                            beparams=beparams)
552
        self.assertEqual(job_id, 3122617 + idx)
553
        self.assertHandler(rlib2.R_2_instances)
554
        self.assertDryRun()
555
        self.assertEqual(self.rapi.CountPending(), 0)
556

    
557
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
558
        self.assertEqual(data["name"], "inst1.example.com")
559
        self.assertEqual(data["disk_template"], "plain")
560
        self.assertEqual(data["pnode"], "node99")
561
        self.assertEqual(data[constants.BE_MEMORY], 512)
562
        self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
563
        self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
564
        self.assertEqual(data[constants.HV_VNC_TLS], True)
565
        self.assertEqual(data["disks"], [disk["size"] for disk in disks])
566

    
567
  def testCreateInstance(self):
568
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
569
    self.rapi.AddResponse("23030")
570
    job_id = self.client.CreateInstance("create", "inst1.example.com",
571
                                        "plain", [], [], dry_run=True)
572
    self.assertEqual(job_id, 23030)
573
    self.assertHandler(rlib2.R_2_instances)
574
    self.assertDryRun()
575

    
576
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
577

    
578
    for field in ["dry_run", "beparams", "hvparams", "start"]:
579
      self.assertFalse(field in data)
580

    
581
    self.assertEqual(data["name"], "inst1.example.com")
582
    self.assertEqual(data["disk_template"], "plain")
583

    
584
  def testCreateInstance2(self):
585
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
586
    self.rapi.AddResponse("24740")
587
    job_id = self.client.CreateInstance("import", "inst2.example.com",
588
                                        "drbd8", [{"size": 100,}],
589
                                        [{}, {"bridge": "br1", }],
590
                                        dry_run=False, start=True,
591
                                        pnode="node1", snode="node9",
592
                                        ip_check=False)
593
    self.assertEqual(job_id, 24740)
594
    self.assertHandler(rlib2.R_2_instances)
595

    
596
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
597
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
598
    self.assertEqual(data["name"], "inst2.example.com")
599
    self.assertEqual(data["disk_template"], "drbd8")
600
    self.assertEqual(data["start"], True)
601
    self.assertEqual(data["ip_check"], False)
602
    self.assertEqualValues(data["disks"], [{"size": 100,}])
603
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
604

    
605
  def testDeleteInstance(self):
606
    self.rapi.AddResponse("1234")
607
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
608
    self.assertHandler(rlib2.R_2_instances_name)
609
    self.assertItems(["instance"])
610
    self.assertDryRun()
611

    
612
  def testGetInstanceTags(self):
613
    self.rapi.AddResponse("[]")
614
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
615
    self.assertHandler(rlib2.R_2_instances_name_tags)
616
    self.assertItems(["fooinstance"])
617

    
618
  def testAddInstanceTags(self):
619
    self.rapi.AddResponse("1234")
620
    self.assertEqual(1234,
621
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
622
    self.assertHandler(rlib2.R_2_instances_name_tags)
623
    self.assertItems(["fooinstance"])
624
    self.assertDryRun()
625
    self.assertQuery("tag", ["awesome"])
626

    
627
  def testDeleteInstanceTags(self):
628
    self.rapi.AddResponse("25826")
629
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
630
                                                           dry_run=True))
631
    self.assertHandler(rlib2.R_2_instances_name_tags)
632
    self.assertItems(["foo"])
633
    self.assertDryRun()
634
    self.assertQuery("tag", ["awesome"])
635

    
636
  def testRebootInstance(self):
637
    self.rapi.AddResponse("6146")
638
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
639
                                        ignore_secondaries=True, dry_run=True)
640
    self.assertEqual(6146, job_id)
641
    self.assertHandler(rlib2.R_2_instances_name_reboot)
642
    self.assertItems(["i-bar"])
643
    self.assertDryRun()
644
    self.assertQuery("type", ["hard"])
645
    self.assertQuery("ignore_secondaries", ["1"])
646

    
647
  def testShutdownInstance(self):
648
    self.rapi.AddResponse("1487")
649
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
650
                                                        dry_run=True))
651
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
652
    self.assertItems(["foo-instance"])
653
    self.assertDryRun()
654

    
655
  def testStartupInstance(self):
656
    self.rapi.AddResponse("27149")
657
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
658
                                                        dry_run=True))
659
    self.assertHandler(rlib2.R_2_instances_name_startup)
660
    self.assertItems(["bar-instance"])
661
    self.assertDryRun()
662

    
663
  def testReinstallInstance(self):
664
    self.rapi.AddResponse(serializer.DumpJson([]))
665
    self.rapi.AddResponse("19119")
666
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
667
                                                          os="DOS",
668
                                                          no_startup=True))
669
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
670
    self.assertItems(["baz-instance"])
671
    self.assertQuery("os", ["DOS"])
672
    self.assertQuery("nostartup", ["1"])
673
    self.assertEqual(self.rapi.CountPending(), 0)
674

    
675
  def testReinstallInstanceNew(self):
676
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
677
    self.rapi.AddResponse("25689")
678
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
679
                                                          os="Debian",
680
                                                          no_startup=True))
681
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
682
    self.assertItems(["moo-instance"])
683
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
684
    self.assertEqual(len(data), 2)
685
    self.assertEqual(data["os"], "Debian")
686
    self.assertEqual(data["start"], False)
687
    self.assertEqual(self.rapi.CountPending(), 0)
688

    
689
  def testReinstallInstanceWithOsparams1(self):
690
    self.rapi.AddResponse(serializer.DumpJson([]))
691
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
692
                      "doo-instance", osparams={"x": "y"})
693
    self.assertEqual(self.rapi.CountPending(), 0)
694

    
695
  def testReinstallInstanceWithOsparams2(self):
696
    osparams = {
697
      "Hello": "World",
698
      "foo": "bar",
699
      }
700
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
701
    self.rapi.AddResponse("1717")
702
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
703
                                                         osparams=osparams))
704
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
705
    self.assertItems(["zoo-instance"])
706
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
707
    self.assertEqual(len(data), 2)
708
    self.assertEqual(data["osparams"], osparams)
709
    self.assertEqual(data["start"], True)
710
    self.assertEqual(self.rapi.CountPending(), 0)
711

    
712
  def testReplaceInstanceDisks(self):
713
    self.rapi.AddResponse("999")
714
    job_id = self.client.ReplaceInstanceDisks("instance-name",
715
        disks=[0, 1], dry_run=True, iallocator="hail")
716
    self.assertEqual(999, job_id)
717
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
718
    self.assertItems(["instance-name"])
719
    self.assertQuery("disks", ["0,1"])
720
    self.assertQuery("mode", ["replace_auto"])
721
    self.assertQuery("iallocator", ["hail"])
722
    self.assertDryRun()
723

    
724
    self.rapi.AddResponse("1000")
725
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
726
        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
727
        dry_run=True)
728
    self.assertEqual(1000, job_id)
729
    self.assertItems(["instance-bar"])
730
    self.assertQuery("disks", ["1"])
731
    self.assertQuery("remote_node", ["foo-node"])
732
    self.assertDryRun()
733

    
734
    self.rapi.AddResponse("5175")
735
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
736
    self.assertItems(["instance-moo"])
737
    self.assertQuery("disks", None)
738

    
739
  def testPrepareExport(self):
740
    self.rapi.AddResponse("8326")
741
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
742
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
743
    self.assertItems(["inst1"])
744
    self.assertQuery("mode", ["local"])
745

    
746
  def testExportInstance(self):
747
    self.rapi.AddResponse("19695")
748
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
749
                                        shutdown=True)
750
    self.assertEqual(job_id, 19695)
751
    self.assertHandler(rlib2.R_2_instances_name_export)
752
    self.assertItems(["inst2"])
753

    
754
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
755
    self.assertEqual(data["mode"], "local")
756
    self.assertEqual(data["destination"], "nodeX")
757
    self.assertEqual(data["shutdown"], True)
758

    
759
  def testMigrateInstanceDefaults(self):
760
    self.rapi.AddResponse("24873")
761
    job_id = self.client.MigrateInstance("inst91")
762
    self.assertEqual(job_id, 24873)
763
    self.assertHandler(rlib2.R_2_instances_name_migrate)
764
    self.assertItems(["inst91"])
765

    
766
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
767
    self.assertFalse(data)
768

    
769
  def testMigrateInstance(self):
770
    for mode in constants.HT_MIGRATION_MODES:
771
      for cleanup in [False, True]:
772
        self.rapi.AddResponse("31910")
773
        job_id = self.client.MigrateInstance("inst289", mode=mode,
774
                                             cleanup=cleanup)
775
        self.assertEqual(job_id, 31910)
776
        self.assertHandler(rlib2.R_2_instances_name_migrate)
777
        self.assertItems(["inst289"])
778

    
779
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
780
        self.assertEqual(len(data), 2)
781
        self.assertEqual(data["mode"], mode)
782
        self.assertEqual(data["cleanup"], cleanup)
783

    
784
  def testRenameInstanceDefaults(self):
785
    new_name = "newnametha7euqu"
786
    self.rapi.AddResponse("8791")
787
    job_id = self.client.RenameInstance("inst18821", new_name)
788
    self.assertEqual(job_id, 8791)
789
    self.assertHandler(rlib2.R_2_instances_name_rename)
790
    self.assertItems(["inst18821"])
791

    
792
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
793
    self.assertEqualValues(data, {"new_name": new_name, })
794

    
795
  def testRenameInstance(self):
796
    new_name = "new-name-yiux1iin"
797
    for ip_check in [False, True]:
798
      for name_check in [False, True]:
799
        self.rapi.AddResponse("24776")
800
        job_id = self.client.RenameInstance("inst20967", new_name,
801
                                             ip_check=ip_check,
802
                                             name_check=name_check)
803
        self.assertEqual(job_id, 24776)
804
        self.assertHandler(rlib2.R_2_instances_name_rename)
805
        self.assertItems(["inst20967"])
806

    
807
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
808
        self.assertEqual(len(data), 3)
809
        self.assertEqual(data["new_name"], new_name)
810
        self.assertEqual(data["ip_check"], ip_check)
811
        self.assertEqual(data["name_check"], name_check)
812

    
813
  def testGetJobs(self):
814
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
815
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
816
    self.assertEqual([123, 124], self.client.GetJobs())
817
    self.assertHandler(rlib2.R_2_jobs)
818

    
819
  def testGetJobStatus(self):
820
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
821
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
822
    self.assertHandler(rlib2.R_2_jobs_id)
823
    self.assertItems(["1234"])
824

    
825
  def testWaitForJobChange(self):
826
    fields = ["id", "summary"]
827
    expected = {
828
      "job_info": [123, "something"],
829
      "log_entries": [],
830
      }
831

    
832
    self.rapi.AddResponse(serializer.DumpJson(expected))
833
    result = self.client.WaitForJobChange(123, fields, [], -1)
834
    self.assertEqualValues(expected, result)
835
    self.assertHandler(rlib2.R_2_jobs_id_wait)
836
    self.assertItems(["123"])
837

    
838
  def testCancelJob(self):
839
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
840
    self.assertEqual([True, "Job 123 will be canceled"],
841
                     self.client.CancelJob(999, dry_run=True))
842
    self.assertHandler(rlib2.R_2_jobs_id)
843
    self.assertItems(["999"])
844
    self.assertDryRun()
845

    
846
  def testGetNodes(self):
847
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
848
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
849
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
850
    self.assertHandler(rlib2.R_2_nodes)
851

    
852
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
853
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
854
    self.assertEqual([{"id": "node1", "uri": "uri1"},
855
                      {"id": "node2", "uri": "uri2"}],
856
                     self.client.GetNodes(bulk=True))
857
    self.assertHandler(rlib2.R_2_nodes)
858
    self.assertBulk()
859

    
860
  def testGetNode(self):
861
    self.rapi.AddResponse("{}")
862
    self.assertEqual({}, self.client.GetNode("node-foo"))
863
    self.assertHandler(rlib2.R_2_nodes_name)
864
    self.assertItems(["node-foo"])
865

    
866
  def testEvacuateNode(self):
867
    self.rapi.AddResponse("9876")
868
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
869
    self.assertEqual(9876, job_id)
870
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
871
    self.assertItems(["node-1"])
872
    self.assertQuery("remote_node", ["node-2"])
873

    
874
    self.rapi.AddResponse("8888")
875
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
876
    self.assertEqual(8888, job_id)
877
    self.assertItems(["node-3"])
878
    self.assertQuery("iallocator", ["hail"])
879
    self.assertDryRun()
880

    
881
    self.assertRaises(client.GanetiApiError,
882
                      self.client.EvacuateNode,
883
                      "node-4", iallocator="hail", remote_node="node-5")
884

    
885
  def testMigrateNode(self):
886
    self.rapi.AddResponse("1111")
887
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
888
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
889
    self.assertItems(["node-a"])
890
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
891
    self.assertDryRun()
892

    
893
    self.rapi.AddResponse("1112")
894
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
895
                                                   mode="live"))
896
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
897
    self.assertItems(["node-a"])
898
    self.assertQuery("mode", ["live"])
899
    self.assertDryRun()
900

    
901
  def testGetNodeRole(self):
902
    self.rapi.AddResponse("\"master\"")
903
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
904
    self.assertHandler(rlib2.R_2_nodes_name_role)
905
    self.assertItems(["node-a"])
906

    
907
  def testSetNodeRole(self):
908
    self.rapi.AddResponse("789")
909
    self.assertEqual(789,
910
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
911
    self.assertHandler(rlib2.R_2_nodes_name_role)
912
    self.assertItems(["node-foo"])
913
    self.assertQuery("force", ["1"])
914
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
915

    
916
  def testGetNodeStorageUnits(self):
917
    self.rapi.AddResponse("42")
918
    self.assertEqual(42,
919
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
920
    self.assertHandler(rlib2.R_2_nodes_name_storage)
921
    self.assertItems(["node-x"])
922
    self.assertQuery("storage_type", ["lvm-pv"])
923
    self.assertQuery("output_fields", ["fields"])
924

    
925
  def testModifyNodeStorageUnits(self):
926
    self.rapi.AddResponse("14")
927
    self.assertEqual(14,
928
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
929
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
930
    self.assertItems(["node-z"])
931
    self.assertQuery("storage_type", ["lvm-pv"])
932
    self.assertQuery("name", ["hda"])
933
    self.assertQuery("allocatable", None)
934

    
935
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
936
      self.rapi.AddResponse("7205")
937
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
938
                                                  allocatable=allocatable)
939
      self.assertEqual(7205, job_id)
940
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
941
      self.assertItems(["node-z"])
942
      self.assertQuery("storage_type", ["lvm-pv"])
943
      self.assertQuery("name", ["hda"])
944
      self.assertQuery("allocatable", [query_allocatable])
945

    
946
  def testRepairNodeStorageUnits(self):
947
    self.rapi.AddResponse("99")
948
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
949
                                                            "hda"))
950
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
951
    self.assertItems(["node-z"])
952
    self.assertQuery("storage_type", ["lvm-pv"])
953
    self.assertQuery("name", ["hda"])
954

    
955
  def testGetNodeTags(self):
956
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
957
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
958
    self.assertHandler(rlib2.R_2_nodes_name_tags)
959
    self.assertItems(["node-k"])
960

    
961
  def testAddNodeTags(self):
962
    self.rapi.AddResponse("1234")
963
    self.assertEqual(1234,
964
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
965
    self.assertHandler(rlib2.R_2_nodes_name_tags)
966
    self.assertItems(["node-v"])
967
    self.assertDryRun()
968
    self.assertQuery("tag", ["awesome"])
969

    
970
  def testDeleteNodeTags(self):
971
    self.rapi.AddResponse("16861")
972
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
973
                                                       dry_run=True))
974
    self.assertHandler(rlib2.R_2_nodes_name_tags)
975
    self.assertItems(["node-w"])
976
    self.assertDryRun()
977
    self.assertQuery("tag", ["awesome"])
978

    
979

    
980
if __name__ == '__main__':
981
  client.UsesRapiClient(testutils.GanetiTestProgram)()