Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 42d4d8b9

History | View | Annotate | Download (51.5 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
from ganeti import utils
34
from ganeti import query
35
from ganeti import objects
36

    
37
from ganeti.rapi import connector
38
from ganeti.rapi import rlib2
39
from ganeti.rapi import client
40

    
41
import testutils
42

    
43

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

    
46
# List of resource handlers which aren't used by the RAPI client
47
_KNOWN_UNUSED = set([
48
  rlib2.R_root,
49
  rlib2.R_2,
50
  ])
51

    
52
# Global variable for collecting used handlers
53
_used_handlers = None
54

    
55

    
56
def _GetPathFromUri(uri):
57
  """Gets the path and query from a URI.
58

59
  """
60
  match = _URI_RE.match(uri)
61
  if match:
62
    return match.groupdict()["path"]
63
  else:
64
    return None
65

    
66

    
67
class FakeCurl:
68
  def __init__(self, rapi):
69
    self._rapi = rapi
70
    self._opts = {}
71
    self._info = {}
72

    
73
  def setopt(self, opt, value):
74
    self._opts[opt] = value
75

    
76
  def getopt(self, opt):
77
    return self._opts.get(opt)
78

    
79
  def unsetopt(self, opt):
80
    self._opts.pop(opt, None)
81

    
82
  def getinfo(self, info):
83
    return self._info[info]
84

    
85
  def perform(self):
86
    method = self._opts[pycurl.CUSTOMREQUEST]
87
    url = self._opts[pycurl.URL]
88
    request_body = self._opts[pycurl.POSTFIELDS]
89
    writefn = self._opts[pycurl.WRITEFUNCTION]
90

    
91
    path = _GetPathFromUri(url)
92
    (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
93

    
94
    self._info[pycurl.RESPONSE_CODE] = code
95
    if resp_body is not None:
96
      writefn(resp_body)
97

    
98

    
99
class RapiMock(object):
100
  def __init__(self):
101
    self._mapper = connector.Mapper()
102
    self._responses = []
103
    self._last_handler = None
104
    self._last_req_data = None
105

    
106
  def ResetResponses(self):
107
    del self._responses[:]
108

    
109
  def AddResponse(self, response, code=200):
110
    self._responses.insert(0, (code, response))
111

    
112
  def CountPending(self):
113
    return len(self._responses)
114

    
115
  def GetLastHandler(self):
116
    return self._last_handler
117

    
118
  def GetLastRequestData(self):
119
    return self._last_req_data
120

    
121
  def FetchResponse(self, path, method, request_body):
122
    self._last_req_data = request_body
123

    
124
    try:
125
      (handler_cls, items, args) = self._mapper.getController(path)
126

    
127
      # Record handler as used
128
      _used_handlers.add(handler_cls)
129

    
130
      self._last_handler = handler_cls(items, args, None)
131
      if not hasattr(self._last_handler, method.upper()):
132
        raise http.HttpNotImplemented(message="Method not implemented")
133

    
134
    except http.HttpException, ex:
135
      code = ex.code
136
      response = ex.message
137
    else:
138
      if not self._responses:
139
        raise Exception("No responses")
140

    
141
      (code, response) = self._responses.pop()
142

    
143
    return code, response
144

    
145

    
146
class TestConstants(unittest.TestCase):
147
  def test(self):
148
    self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
149
    self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
150
    self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
151
    self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
152
    self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
153
    self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
154
    self.assertEqual(client._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
155
    self.assertEqual(client._NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
156
    self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
157
    self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
158
    self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
159
    self.assertEqual(client.JOB_STATUS_CANCELING,
160
                     constants.JOB_STATUS_CANCELING)
161
    self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
162
    self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
163
    self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
164
    self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
165
    self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
166
    self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
167

    
168
    # Legacy name
169
    self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITING)
170

    
171

    
172
class RapiMockTest(unittest.TestCase):
173
  def test(self):
174
    rapi = RapiMock()
175
    path = "/version"
176
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
177
    self.assertEqual((501, "Method not implemented"),
178
                     rapi.FetchResponse("/version", "POST", None))
179
    rapi.AddResponse("2")
180
    code, response = rapi.FetchResponse("/version", "GET", None)
181
    self.assertEqual(200, code)
182
    self.assertEqual("2", response)
183
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
184

    
185

    
186
def _FakeNoSslPycurlVersion():
187
  # Note: incomplete version tuple
188
  return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
189

    
190

    
191
def _FakeFancySslPycurlVersion():
192
  # Note: incomplete version tuple
193
  return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
194

    
195

    
196
def _FakeOpenSslPycurlVersion():
197
  # Note: incomplete version tuple
198
  return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
199

    
200

    
201
def _FakeGnuTlsPycurlVersion():
202
  # Note: incomplete version tuple
203
  return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
204

    
205

    
206
class TestExtendedConfig(unittest.TestCase):
207
  def testAuth(self):
208
    cl = client.GanetiRapiClient("master.example.com",
209
                                 username="user", password="pw",
210
                                 curl_factory=lambda: FakeCurl(RapiMock()))
211

    
212
    curl = cl._CreateCurl()
213
    self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
214
    self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
215

    
216
  def testInvalidAuth(self):
217
    # No username
218
    self.assertRaises(client.Error, client.GanetiRapiClient,
219
                      "master-a.example.com", password="pw")
220
    # No password
221
    self.assertRaises(client.Error, client.GanetiRapiClient,
222
                      "master-b.example.com", username="user")
223

    
224
  def testCertVerifyInvalidCombinations(self):
225
    self.assertRaises(client.Error, client.GenericCurlConfig,
226
                      use_curl_cabundle=True, cafile="cert1.pem")
227
    self.assertRaises(client.Error, client.GenericCurlConfig,
228
                      use_curl_cabundle=True, capath="certs/")
229
    self.assertRaises(client.Error, client.GenericCurlConfig,
230
                      use_curl_cabundle=True,
231
                      cafile="cert1.pem", capath="certs/")
232

    
233
  def testProxySignalVerifyHostname(self):
234
    for use_gnutls in [False, True]:
235
      if use_gnutls:
236
        pcverfn = _FakeGnuTlsPycurlVersion
237
      else:
238
        pcverfn = _FakeOpenSslPycurlVersion
239

    
240
      for proxy in ["", "http://127.0.0.1:1234"]:
241
        for use_signal in [False, True]:
242
          for verify_hostname in [False, True]:
243
            cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
244
                                             verify_hostname=verify_hostname,
245
                                             _pycurl_version_fn=pcverfn)
246

    
247
            curl_factory = lambda: FakeCurl(RapiMock())
248
            cl = client.GanetiRapiClient("master.example.com",
249
                                         curl_config_fn=cfgfn,
250
                                         curl_factory=curl_factory)
251

    
252
            curl = cl._CreateCurl()
253
            self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
254
            self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
255

    
256
            if verify_hostname:
257
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
258
            else:
259
              self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
260

    
261
  def testNoCertVerify(self):
262
    cfgfn = client.GenericCurlConfig()
263

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

    
268
    curl = cl._CreateCurl()
269
    self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
270
    self.assertFalse(curl.getopt(pycurl.CAINFO))
271
    self.assertFalse(curl.getopt(pycurl.CAPATH))
272

    
273
  def testCertVerifyCurlBundle(self):
274
    cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
275

    
276
    curl_factory = lambda: FakeCurl(RapiMock())
277
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
278
                                 curl_factory=curl_factory)
279

    
280
    curl = cl._CreateCurl()
281
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
282
    self.assertFalse(curl.getopt(pycurl.CAINFO))
283
    self.assertFalse(curl.getopt(pycurl.CAPATH))
284

    
285
  def testCertVerifyCafile(self):
286
    mycert = "/tmp/some/UNUSED/cert/file.pem"
287
    cfgfn = client.GenericCurlConfig(cafile=mycert)
288

    
289
    curl_factory = lambda: FakeCurl(RapiMock())
290
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
291
                                 curl_factory=curl_factory)
292

    
293
    curl = cl._CreateCurl()
294
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
295
    self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
296
    self.assertFalse(curl.getopt(pycurl.CAPATH))
297

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

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

    
308
    curl = cl._CreateCurl()
309
    self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
310
    self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
311
    self.assertFalse(curl.getopt(pycurl.CAINFO))
312

    
313
  def testCertVerifyCapathGnuTls(self):
314
    certdir = "/tmp/some/UNUSED/cert/directory"
315
    pcverfn = _FakeGnuTlsPycurlVersion
316
    cfgfn = client.GenericCurlConfig(capath=certdir,
317
                                     _pycurl_version_fn=pcverfn)
318

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

    
323
    self.assertRaises(client.Error, cl._CreateCurl)
324

    
325
  def testCertVerifyNoSsl(self):
326
    certdir = "/tmp/some/UNUSED/cert/directory"
327
    pcverfn = _FakeNoSslPycurlVersion
328
    cfgfn = client.GenericCurlConfig(capath=certdir,
329
                                     _pycurl_version_fn=pcverfn)
330

    
331
    curl_factory = lambda: FakeCurl(RapiMock())
332
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
333
                                 curl_factory=curl_factory)
334

    
335
    self.assertRaises(client.Error, cl._CreateCurl)
336

    
337
  def testCertVerifyFancySsl(self):
338
    certdir = "/tmp/some/UNUSED/cert/directory"
339
    pcverfn = _FakeFancySslPycurlVersion
340
    cfgfn = client.GenericCurlConfig(capath=certdir,
341
                                     _pycurl_version_fn=pcverfn)
342

    
343
    curl_factory = lambda: FakeCurl(RapiMock())
344
    cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
345
                                 curl_factory=curl_factory)
346

    
347
    self.assertRaises(NotImplementedError, cl._CreateCurl)
348

    
349
  def testCertVerifyCapath(self):
350
    for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
351
      for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
352
        cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
353
                                         timeout=timeout)
354

    
355
        curl_factory = lambda: FakeCurl(RapiMock())
356
        cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
357
                                     curl_factory=curl_factory)
358

    
359
        curl = cl._CreateCurl()
360
        self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
361
        self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
362

    
363

    
364
class GanetiRapiClientTests(testutils.GanetiTestCase):
365
  def setUp(self):
366
    testutils.GanetiTestCase.setUp(self)
367

    
368
    self.rapi = RapiMock()
369
    self.curl = FakeCurl(self.rapi)
370
    self.client = client.GanetiRapiClient("master.example.com",
371
                                          curl_factory=lambda: self.curl)
372

    
373
  def assertHandler(self, handler_cls):
374
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
375

    
376
  def assertQuery(self, key, value):
377
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
378

    
379
  def assertItems(self, items):
380
    self.assertEqual(items, self.rapi.GetLastHandler().items)
381

    
382
  def assertBulk(self):
383
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
384

    
385
  def assertDryRun(self):
386
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
387

    
388
  def assertUseForce(self):
389
    self.assertTrue(self.rapi.GetLastHandler().useForce())
390

    
391
  def testEncodeQuery(self):
392
    query = [
393
      ("a", None),
394
      ("b", 1),
395
      ("c", 2),
396
      ("d", "Foo"),
397
      ("e", True),
398
      ]
399

    
400
    expected = [
401
      ("a", ""),
402
      ("b", 1),
403
      ("c", 2),
404
      ("d", "Foo"),
405
      ("e", 1),
406
      ]
407

    
408
    self.assertEqualValues(self.client._EncodeQuery(query),
409
                           expected)
410

    
411
    # invalid types
412
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
413
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
414

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

    
420
    # Signals should be disabled by default
421
    self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
422

    
423
    # No auth and no proxy
424
    self.assertFalse(self.curl.getopt(pycurl.USERPWD))
425
    self.assert_(self.curl.getopt(pycurl.PROXY) is None)
426

    
427
    # Content-type is required for requests
428
    headers = self.curl.getopt(pycurl.HTTPHEADER)
429
    self.assert_("Content-type: application/json" in headers)
430

    
431
  def testHttpError(self):
432
    self.rapi.AddResponse(None, code=404)
433
    try:
434
      self.client.GetJobStatus(15140)
435
    except client.GanetiApiError, err:
436
      self.assertEqual(err.code, 404)
437
    else:
438
      self.fail("Didn't raise exception")
439

    
440
  def testGetVersion(self):
441
    self.rapi.AddResponse("2")
442
    self.assertEqual(2, self.client.GetVersion())
443
    self.assertHandler(rlib2.R_version)
444

    
445
  def testGetFeatures(self):
446
    for features in [[], ["foo", "bar", "baz"]]:
447
      self.rapi.AddResponse(serializer.DumpJson(features))
448
      self.assertEqual(features, self.client.GetFeatures())
449
      self.assertHandler(rlib2.R_2_features)
450

    
451
  def testGetFeaturesNotFound(self):
452
    self.rapi.AddResponse(None, code=404)
453
    self.assertEqual([], self.client.GetFeatures())
454

    
455
  def testGetOperatingSystems(self):
456
    self.rapi.AddResponse("[\"beos\"]")
457
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
458
    self.assertHandler(rlib2.R_2_os)
459

    
460
  def testGetClusterTags(self):
461
    self.rapi.AddResponse("[\"tag\"]")
462
    self.assertEqual(["tag"], self.client.GetClusterTags())
463
    self.assertHandler(rlib2.R_2_tags)
464

    
465
  def testAddClusterTags(self):
466
    self.rapi.AddResponse("1234")
467
    self.assertEqual(1234,
468
        self.client.AddClusterTags(["awesome"], dry_run=True))
469
    self.assertHandler(rlib2.R_2_tags)
470
    self.assertDryRun()
471
    self.assertQuery("tag", ["awesome"])
472

    
473
  def testDeleteClusterTags(self):
474
    self.rapi.AddResponse("5107")
475
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
476
                                                         dry_run=True))
477
    self.assertHandler(rlib2.R_2_tags)
478
    self.assertDryRun()
479
    self.assertQuery("tag", ["awesome"])
480

    
481
  def testGetInfo(self):
482
    self.rapi.AddResponse("{}")
483
    self.assertEqual({}, self.client.GetInfo())
484
    self.assertHandler(rlib2.R_2_info)
485

    
486
  def testGetInstances(self):
487
    self.rapi.AddResponse("[]")
488
    self.assertEqual([], self.client.GetInstances(bulk=True))
489
    self.assertHandler(rlib2.R_2_instances)
490
    self.assertBulk()
491

    
492
  def testGetInstance(self):
493
    self.rapi.AddResponse("[]")
494
    self.assertEqual([], self.client.GetInstance("instance"))
495
    self.assertHandler(rlib2.R_2_instances_name)
496
    self.assertItems(["instance"])
497

    
498
  def testGetInstanceInfo(self):
499
    self.rapi.AddResponse("21291")
500
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
501
    self.assertHandler(rlib2.R_2_instances_name_info)
502
    self.assertItems(["inst3"])
503
    self.assertQuery("static", None)
504

    
505
    self.rapi.AddResponse("3428")
506
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
507
    self.assertHandler(rlib2.R_2_instances_name_info)
508
    self.assertItems(["inst31"])
509
    self.assertQuery("static", ["0"])
510

    
511
    self.rapi.AddResponse("15665")
512
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
513
    self.assertHandler(rlib2.R_2_instances_name_info)
514
    self.assertItems(["inst32"])
515
    self.assertQuery("static", ["1"])
516

    
517
  def testCreateInstanceOldVersion(self):
518
    # The old request format, version 0, is no longer supported
519
    self.rapi.AddResponse(None, code=404)
520
    self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
521
                      "create", "inst1.example.com", "plain", [], [])
522
    self.assertEqual(self.rapi.CountPending(), 0)
523

    
524
  def testCreateInstance(self):
525
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
526
    self.rapi.AddResponse("23030")
527
    job_id = self.client.CreateInstance("create", "inst1.example.com",
528
                                        "plain", [], [], dry_run=True)
529
    self.assertEqual(job_id, 23030)
530
    self.assertHandler(rlib2.R_2_instances)
531
    self.assertDryRun()
532

    
533
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
534

    
535
    for field in ["dry_run", "beparams", "hvparams", "start"]:
536
      self.assertFalse(field in data)
537

    
538
    self.assertEqual(data["name"], "inst1.example.com")
539
    self.assertEqual(data["disk_template"], "plain")
540

    
541
  def testCreateInstance2(self):
542
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
543
    self.rapi.AddResponse("24740")
544
    job_id = self.client.CreateInstance("import", "inst2.example.com",
545
                                        "drbd8", [{"size": 100,}],
546
                                        [{}, {"bridge": "br1", }],
547
                                        dry_run=False, start=True,
548
                                        pnode="node1", snode="node9",
549
                                        ip_check=False)
550
    self.assertEqual(job_id, 24740)
551
    self.assertHandler(rlib2.R_2_instances)
552

    
553
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
554
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
555
    self.assertEqual(data["name"], "inst2.example.com")
556
    self.assertEqual(data["disk_template"], "drbd8")
557
    self.assertEqual(data["start"], True)
558
    self.assertEqual(data["ip_check"], False)
559
    self.assertEqualValues(data["disks"], [{"size": 100,}])
560
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
561

    
562
  def testDeleteInstance(self):
563
    self.rapi.AddResponse("1234")
564
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
565
    self.assertHandler(rlib2.R_2_instances_name)
566
    self.assertItems(["instance"])
567
    self.assertDryRun()
568

    
569
  def testGetInstanceTags(self):
570
    self.rapi.AddResponse("[]")
571
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
572
    self.assertHandler(rlib2.R_2_instances_name_tags)
573
    self.assertItems(["fooinstance"])
574

    
575
  def testAddInstanceTags(self):
576
    self.rapi.AddResponse("1234")
577
    self.assertEqual(1234,
578
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
579
    self.assertHandler(rlib2.R_2_instances_name_tags)
580
    self.assertItems(["fooinstance"])
581
    self.assertDryRun()
582
    self.assertQuery("tag", ["awesome"])
583

    
584
  def testDeleteInstanceTags(self):
585
    self.rapi.AddResponse("25826")
586
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
587
                                                           dry_run=True))
588
    self.assertHandler(rlib2.R_2_instances_name_tags)
589
    self.assertItems(["foo"])
590
    self.assertDryRun()
591
    self.assertQuery("tag", ["awesome"])
592

    
593
  def testRebootInstance(self):
594
    self.rapi.AddResponse("6146")
595
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
596
                                        ignore_secondaries=True, dry_run=True)
597
    self.assertEqual(6146, job_id)
598
    self.assertHandler(rlib2.R_2_instances_name_reboot)
599
    self.assertItems(["i-bar"])
600
    self.assertDryRun()
601
    self.assertQuery("type", ["hard"])
602
    self.assertQuery("ignore_secondaries", ["1"])
603

    
604
  def testShutdownInstance(self):
605
    self.rapi.AddResponse("1487")
606
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
607
                                                        dry_run=True))
608
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
609
    self.assertItems(["foo-instance"])
610
    self.assertDryRun()
611

    
612
  def testStartupInstance(self):
613
    self.rapi.AddResponse("27149")
614
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
615
                                                        dry_run=True))
616
    self.assertHandler(rlib2.R_2_instances_name_startup)
617
    self.assertItems(["bar-instance"])
618
    self.assertDryRun()
619

    
620
  def testReinstallInstance(self):
621
    self.rapi.AddResponse(serializer.DumpJson([]))
622
    self.rapi.AddResponse("19119")
623
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
624
                                                          os="DOS",
625
                                                          no_startup=True))
626
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
627
    self.assertItems(["baz-instance"])
628
    self.assertQuery("os", ["DOS"])
629
    self.assertQuery("nostartup", ["1"])
630
    self.assertEqual(self.rapi.CountPending(), 0)
631

    
632
  def testReinstallInstanceNew(self):
633
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
634
    self.rapi.AddResponse("25689")
635
    self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
636
                                                          os="Debian",
637
                                                          no_startup=True))
638
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
639
    self.assertItems(["moo-instance"])
640
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
641
    self.assertEqual(len(data), 2)
642
    self.assertEqual(data["os"], "Debian")
643
    self.assertEqual(data["start"], False)
644
    self.assertEqual(self.rapi.CountPending(), 0)
645

    
646
  def testReinstallInstanceWithOsparams1(self):
647
    self.rapi.AddResponse(serializer.DumpJson([]))
648
    self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
649
                      "doo-instance", osparams={"x": "y"})
650
    self.assertEqual(self.rapi.CountPending(), 0)
651

    
652
  def testReinstallInstanceWithOsparams2(self):
653
    osparams = {
654
      "Hello": "World",
655
      "foo": "bar",
656
      }
657
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
658
    self.rapi.AddResponse("1717")
659
    self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
660
                                                         osparams=osparams))
661
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
662
    self.assertItems(["zoo-instance"])
663
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
664
    self.assertEqual(len(data), 2)
665
    self.assertEqual(data["osparams"], osparams)
666
    self.assertEqual(data["start"], True)
667
    self.assertEqual(self.rapi.CountPending(), 0)
668

    
669
  def testReplaceInstanceDisks(self):
670
    self.rapi.AddResponse("999")
671
    job_id = self.client.ReplaceInstanceDisks("instance-name",
672
        disks=[0, 1], dry_run=True, iallocator="hail")
673
    self.assertEqual(999, job_id)
674
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
675
    self.assertItems(["instance-name"])
676
    self.assertQuery("disks", ["0,1"])
677
    self.assertQuery("mode", ["replace_auto"])
678
    self.assertQuery("iallocator", ["hail"])
679
    self.assertDryRun()
680

    
681
    self.rapi.AddResponse("1000")
682
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
683
        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
684
        dry_run=True)
685
    self.assertEqual(1000, job_id)
686
    self.assertItems(["instance-bar"])
687
    self.assertQuery("disks", ["1"])
688
    self.assertQuery("remote_node", ["foo-node"])
689
    self.assertDryRun()
690

    
691
    self.rapi.AddResponse("5175")
692
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
693
    self.assertItems(["instance-moo"])
694
    self.assertQuery("disks", None)
695

    
696
  def testPrepareExport(self):
697
    self.rapi.AddResponse("8326")
698
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
699
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
700
    self.assertItems(["inst1"])
701
    self.assertQuery("mode", ["local"])
702

    
703
  def testExportInstance(self):
704
    self.rapi.AddResponse("19695")
705
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
706
                                        shutdown=True)
707
    self.assertEqual(job_id, 19695)
708
    self.assertHandler(rlib2.R_2_instances_name_export)
709
    self.assertItems(["inst2"])
710

    
711
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
712
    self.assertEqual(data["mode"], "local")
713
    self.assertEqual(data["destination"], "nodeX")
714
    self.assertEqual(data["shutdown"], True)
715

    
716
  def testMigrateInstanceDefaults(self):
717
    self.rapi.AddResponse("24873")
718
    job_id = self.client.MigrateInstance("inst91")
719
    self.assertEqual(job_id, 24873)
720
    self.assertHandler(rlib2.R_2_instances_name_migrate)
721
    self.assertItems(["inst91"])
722

    
723
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
724
    self.assertFalse(data)
725

    
726
  def testMigrateInstance(self):
727
    for mode in constants.HT_MIGRATION_MODES:
728
      for cleanup in [False, True]:
729
        self.rapi.AddResponse("31910")
730
        job_id = self.client.MigrateInstance("inst289", mode=mode,
731
                                             cleanup=cleanup)
732
        self.assertEqual(job_id, 31910)
733
        self.assertHandler(rlib2.R_2_instances_name_migrate)
734
        self.assertItems(["inst289"])
735

    
736
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
737
        self.assertEqual(len(data), 2)
738
        self.assertEqual(data["mode"], mode)
739
        self.assertEqual(data["cleanup"], cleanup)
740

    
741
  def testFailoverInstanceDefaults(self):
742
    self.rapi.AddResponse("7639")
743
    job_id = self.client.FailoverInstance("inst13579")
744
    self.assertEqual(job_id, 7639)
745
    self.assertHandler(rlib2.R_2_instances_name_failover)
746
    self.assertItems(["inst13579"])
747

    
748
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
749
    self.assertFalse(data)
750

    
751
  def testFailoverInstance(self):
752
    for iallocator in ["dumb", "hail"]:
753
      for ignore_consistency in [False, True]:
754
        for target_node in ["node-a", "node2"]:
755
          self.rapi.AddResponse("19161")
756
          job_id = \
757
            self.client.FailoverInstance("inst251", iallocator=iallocator,
758
                                         ignore_consistency=ignore_consistency,
759
                                         target_node=target_node)
760
          self.assertEqual(job_id, 19161)
761
          self.assertHandler(rlib2.R_2_instances_name_failover)
762
          self.assertItems(["inst251"])
763

    
764
          data = serializer.LoadJson(self.rapi.GetLastRequestData())
765
          self.assertEqual(len(data), 3)
766
          self.assertEqual(data["iallocator"], iallocator)
767
          self.assertEqual(data["ignore_consistency"], ignore_consistency)
768
          self.assertEqual(data["target_node"], target_node)
769
          self.assertEqual(self.rapi.CountPending(), 0)
770

    
771
  def testRenameInstanceDefaults(self):
772
    new_name = "newnametha7euqu"
773
    self.rapi.AddResponse("8791")
774
    job_id = self.client.RenameInstance("inst18821", new_name)
775
    self.assertEqual(job_id, 8791)
776
    self.assertHandler(rlib2.R_2_instances_name_rename)
777
    self.assertItems(["inst18821"])
778

    
779
    data = serializer.LoadJson(self.rapi.GetLastRequestData())
780
    self.assertEqualValues(data, {"new_name": new_name, })
781

    
782
  def testRenameInstance(self):
783
    new_name = "new-name-yiux1iin"
784
    for ip_check in [False, True]:
785
      for name_check in [False, True]:
786
        self.rapi.AddResponse("24776")
787
        job_id = self.client.RenameInstance("inst20967", new_name,
788
                                             ip_check=ip_check,
789
                                             name_check=name_check)
790
        self.assertEqual(job_id, 24776)
791
        self.assertHandler(rlib2.R_2_instances_name_rename)
792
        self.assertItems(["inst20967"])
793

    
794
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
795
        self.assertEqual(len(data), 3)
796
        self.assertEqual(data["new_name"], new_name)
797
        self.assertEqual(data["ip_check"], ip_check)
798
        self.assertEqual(data["name_check"], name_check)
799

    
800
  def testGetJobs(self):
801
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
802
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
803
    self.assertEqual([123, 124], self.client.GetJobs())
804
    self.assertHandler(rlib2.R_2_jobs)
805

    
806
  def testGetJobStatus(self):
807
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
808
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
809
    self.assertHandler(rlib2.R_2_jobs_id)
810
    self.assertItems(["1234"])
811

    
812
  def testWaitForJobChange(self):
813
    fields = ["id", "summary"]
814
    expected = {
815
      "job_info": [123, "something"],
816
      "log_entries": [],
817
      }
818

    
819
    self.rapi.AddResponse(serializer.DumpJson(expected))
820
    result = self.client.WaitForJobChange(123, fields, [], -1)
821
    self.assertEqualValues(expected, result)
822
    self.assertHandler(rlib2.R_2_jobs_id_wait)
823
    self.assertItems(["123"])
824

    
825
  def testCancelJob(self):
826
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
827
    self.assertEqual([True, "Job 123 will be canceled"],
828
                     self.client.CancelJob(999, dry_run=True))
829
    self.assertHandler(rlib2.R_2_jobs_id)
830
    self.assertItems(["999"])
831
    self.assertDryRun()
832

    
833
  def testGetNodes(self):
834
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
835
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
836
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
837
    self.assertHandler(rlib2.R_2_nodes)
838

    
839
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
840
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
841
    self.assertEqual([{"id": "node1", "uri": "uri1"},
842
                      {"id": "node2", "uri": "uri2"}],
843
                     self.client.GetNodes(bulk=True))
844
    self.assertHandler(rlib2.R_2_nodes)
845
    self.assertBulk()
846

    
847
  def testGetNode(self):
848
    self.rapi.AddResponse("{}")
849
    self.assertEqual({}, self.client.GetNode("node-foo"))
850
    self.assertHandler(rlib2.R_2_nodes_name)
851
    self.assertItems(["node-foo"])
852

    
853
  def testEvacuateNode(self):
854
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
855
    self.rapi.AddResponse("9876")
856
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
857
    self.assertEqual(9876, job_id)
858
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
859
    self.assertItems(["node-1"])
860
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
861
                     { "remote_node": "node-2", })
862
    self.assertEqual(self.rapi.CountPending(), 0)
863

    
864
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
865
    self.rapi.AddResponse("8888")
866
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
867
    self.assertEqual(8888, job_id)
868
    self.assertItems(["node-3"])
869
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
870
                     { "iallocator": "hail", })
871
    self.assertDryRun()
872

    
873
    self.assertRaises(client.GanetiApiError,
874
                      self.client.EvacuateNode,
875
                      "node-4", iallocator="hail", remote_node="node-5")
876
    self.assertEqual(self.rapi.CountPending(), 0)
877

    
878
  def testEvacuateNodeOldResponse(self):
879
    self.rapi.AddResponse(serializer.DumpJson([]))
880
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
881
                      "node-4", accept_old=False)
882
    self.assertEqual(self.rapi.CountPending(), 0)
883

    
884
    self.rapi.AddResponse(serializer.DumpJson([]))
885
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
886
                      "node-4", accept_old=True)
887
    self.assertEqual(self.rapi.CountPending(), 0)
888

    
889
    self.rapi.AddResponse(serializer.DumpJson([]))
890
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
891
                      "node-4", accept_old=True, primary=True)
892
    self.assertEqual(self.rapi.CountPending(), 0)
893

    
894
    self.rapi.AddResponse(serializer.DumpJson([]))
895
    self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
896
                      "node-4", accept_old=True, secondary=False)
897
    self.assertEqual(self.rapi.CountPending(), 0)
898

    
899
    for sec in [True, None]:
900
      self.rapi.AddResponse(serializer.DumpJson([]))
901
      self.rapi.AddResponse(serializer.DumpJson([["res", "foo"]]))
902
      result = self.client.EvacuateNode("node-3", iallocator="hail",
903
                                        dry_run=True, accept_old=True,
904
                                        primary=False, secondary=sec)
905
      self.assertEqual(result, [["res", "foo"]])
906
      self.assertItems(["node-3"])
907
      self.assertQuery("iallocator", ["hail"])
908
      self.assertFalse(self.rapi.GetLastRequestData())
909
      self.assertDryRun()
910
      self.assertEqual(self.rapi.CountPending(), 0)
911

    
912
  def testMigrateNode(self):
913
    self.rapi.AddResponse(serializer.DumpJson([]))
914
    self.rapi.AddResponse("1111")
915
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
916
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
917
    self.assertItems(["node-a"])
918
    self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
919
    self.assertDryRun()
920
    self.assertFalse(self.rapi.GetLastRequestData())
921

    
922
    self.rapi.AddResponse(serializer.DumpJson([]))
923
    self.rapi.AddResponse("1112")
924
    self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
925
                                                   mode="live"))
926
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
927
    self.assertItems(["node-a"])
928
    self.assertQuery("mode", ["live"])
929
    self.assertDryRun()
930
    self.assertFalse(self.rapi.GetLastRequestData())
931

    
932
    self.rapi.AddResponse(serializer.DumpJson([]))
933
    self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
934
                      "node-c", target_node="foonode")
935
    self.assertEqual(self.rapi.CountPending(), 0)
936

    
937
  def testMigrateNodeBodyData(self):
938
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
939
    self.rapi.AddResponse("27539")
940
    self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
941
                                                    mode="live"))
942
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
943
    self.assertItems(["node-a"])
944
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
945
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
946
                     { "mode": "live", })
947

    
948
    self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
949
    self.rapi.AddResponse("14219")
950
    self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
951
                                                    target_node="node9",
952
                                                    iallocator="ial"))
953
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
954
    self.assertItems(["node-x"])
955
    self.assertDryRun()
956
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
957
                     { "target_node": "node9", "iallocator": "ial", })
958

    
959
    self.assertEqual(self.rapi.CountPending(), 0)
960

    
961
  def testGetNodeRole(self):
962
    self.rapi.AddResponse("\"master\"")
963
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
964
    self.assertHandler(rlib2.R_2_nodes_name_role)
965
    self.assertItems(["node-a"])
966

    
967
  def testSetNodeRole(self):
968
    self.rapi.AddResponse("789")
969
    self.assertEqual(789,
970
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
971
    self.assertHandler(rlib2.R_2_nodes_name_role)
972
    self.assertItems(["node-foo"])
973
    self.assertQuery("force", ["1"])
974
    self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
975

    
976
  def testPowercycleNode(self):
977
    self.rapi.AddResponse("23051")
978
    self.assertEqual(23051,
979
        self.client.PowercycleNode("node5468", force=True))
980
    self.assertHandler(rlib2.R_2_nodes_name_powercycle)
981
    self.assertItems(["node5468"])
982
    self.assertQuery("force", ["1"])
983
    self.assertFalse(self.rapi.GetLastRequestData())
984
    self.assertEqual(self.rapi.CountPending(), 0)
985

    
986
  def testGetNodeStorageUnits(self):
987
    self.rapi.AddResponse("42")
988
    self.assertEqual(42,
989
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
990
    self.assertHandler(rlib2.R_2_nodes_name_storage)
991
    self.assertItems(["node-x"])
992
    self.assertQuery("storage_type", ["lvm-pv"])
993
    self.assertQuery("output_fields", ["fields"])
994

    
995
  def testModifyNodeStorageUnits(self):
996
    self.rapi.AddResponse("14")
997
    self.assertEqual(14,
998
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
999
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1000
    self.assertItems(["node-z"])
1001
    self.assertQuery("storage_type", ["lvm-pv"])
1002
    self.assertQuery("name", ["hda"])
1003
    self.assertQuery("allocatable", None)
1004

    
1005
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
1006
      self.rapi.AddResponse("7205")
1007
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
1008
                                                  allocatable=allocatable)
1009
      self.assertEqual(7205, job_id)
1010
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1011
      self.assertItems(["node-z"])
1012
      self.assertQuery("storage_type", ["lvm-pv"])
1013
      self.assertQuery("name", ["hda"])
1014
      self.assertQuery("allocatable", [query_allocatable])
1015

    
1016
  def testRepairNodeStorageUnits(self):
1017
    self.rapi.AddResponse("99")
1018
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1019
                                                            "hda"))
1020
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1021
    self.assertItems(["node-z"])
1022
    self.assertQuery("storage_type", ["lvm-pv"])
1023
    self.assertQuery("name", ["hda"])
1024

    
1025
  def testGetNodeTags(self):
1026
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
1027
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1028
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1029
    self.assertItems(["node-k"])
1030

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

    
1040
  def testDeleteNodeTags(self):
1041
    self.rapi.AddResponse("16861")
1042
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1043
                                                       dry_run=True))
1044
    self.assertHandler(rlib2.R_2_nodes_name_tags)
1045
    self.assertItems(["node-w"])
1046
    self.assertDryRun()
1047
    self.assertQuery("tag", ["awesome"])
1048

    
1049
  def testGetGroups(self):
1050
    groups = [{"name": "group1",
1051
               "uri": "/2/groups/group1",
1052
               },
1053
              {"name": "group2",
1054
               "uri": "/2/groups/group2",
1055
               },
1056
              ]
1057
    self.rapi.AddResponse(serializer.DumpJson(groups))
1058
    self.assertEqual(["group1", "group2"], self.client.GetGroups())
1059
    self.assertHandler(rlib2.R_2_groups)
1060

    
1061
  def testGetGroupsBulk(self):
1062
    groups = [{"name": "group1",
1063
               "uri": "/2/groups/group1",
1064
               "node_cnt": 2,
1065
               "node_list": ["gnt1.test",
1066
                             "gnt2.test",
1067
                             ],
1068
               },
1069
              {"name": "group2",
1070
               "uri": "/2/groups/group2",
1071
               "node_cnt": 1,
1072
               "node_list": ["gnt3.test",
1073
                             ],
1074
               },
1075
              ]
1076
    self.rapi.AddResponse(serializer.DumpJson(groups))
1077

    
1078
    self.assertEqual(groups, self.client.GetGroups(bulk=True))
1079
    self.assertHandler(rlib2.R_2_groups)
1080
    self.assertBulk()
1081

    
1082
  def testGetGroup(self):
1083
    group = {"ctime": None,
1084
             "name": "default",
1085
             }
1086
    self.rapi.AddResponse(serializer.DumpJson(group))
1087
    self.assertEqual({"ctime": None, "name": "default"},
1088
                     self.client.GetGroup("default"))
1089
    self.assertHandler(rlib2.R_2_groups_name)
1090
    self.assertItems(["default"])
1091

    
1092
  def testCreateGroup(self):
1093
    self.rapi.AddResponse("12345")
1094
    job_id = self.client.CreateGroup("newgroup", dry_run=True)
1095
    self.assertEqual(job_id, 12345)
1096
    self.assertHandler(rlib2.R_2_groups)
1097
    self.assertDryRun()
1098

    
1099
  def testDeleteGroup(self):
1100
    self.rapi.AddResponse("12346")
1101
    job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1102
    self.assertEqual(job_id, 12346)
1103
    self.assertHandler(rlib2.R_2_groups_name)
1104
    self.assertDryRun()
1105

    
1106
  def testRenameGroup(self):
1107
    self.rapi.AddResponse("12347")
1108
    job_id = self.client.RenameGroup("oldname", "newname")
1109
    self.assertEqual(job_id, 12347)
1110
    self.assertHandler(rlib2.R_2_groups_name_rename)
1111

    
1112
  def testModifyGroup(self):
1113
    self.rapi.AddResponse("12348")
1114
    job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1115
    self.assertEqual(job_id, 12348)
1116
    self.assertHandler(rlib2.R_2_groups_name_modify)
1117

    
1118
  def testAssignGroupNodes(self):
1119
    self.rapi.AddResponse("12349")
1120
    job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1121
                                          force=True, dry_run=True)
1122
    self.assertEqual(job_id, 12349)
1123
    self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1124
    self.assertDryRun()
1125
    self.assertUseForce()
1126

    
1127
  def testModifyInstance(self):
1128
    self.rapi.AddResponse("23681")
1129
    job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1130
    self.assertEqual(job_id, 23681)
1131
    self.assertItems(["inst7210"])
1132
    self.assertHandler(rlib2.R_2_instances_name_modify)
1133
    self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1134
                     { "os_name": "linux", })
1135

    
1136
  def testModifyCluster(self):
1137
    for mnh in [None, False, True]:
1138
      self.rapi.AddResponse("14470")
1139
      self.assertEqual(14470,
1140
        self.client.ModifyCluster(maintain_node_health=mnh))
1141
      self.assertHandler(rlib2.R_2_cluster_modify)
1142
      self.assertItems([])
1143
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1144
      self.assertEqual(len(data), 1)
1145
      self.assertEqual(data["maintain_node_health"], mnh)
1146
      self.assertEqual(self.rapi.CountPending(), 0)
1147

    
1148
  def testRedistributeConfig(self):
1149
    self.rapi.AddResponse("3364")
1150
    job_id = self.client.RedistributeConfig()
1151
    self.assertEqual(job_id, 3364)
1152
    self.assertItems([])
1153
    self.assertHandler(rlib2.R_2_redist_config)
1154

    
1155
  def testActivateInstanceDisks(self):
1156
    self.rapi.AddResponse("23547")
1157
    job_id = self.client.ActivateInstanceDisks("inst28204")
1158
    self.assertEqual(job_id, 23547)
1159
    self.assertItems(["inst28204"])
1160
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1161
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1162

    
1163
  def testActivateInstanceDisksIgnoreSize(self):
1164
    self.rapi.AddResponse("11044")
1165
    job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1166
    self.assertEqual(job_id, 11044)
1167
    self.assertItems(["inst28204"])
1168
    self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1169
    self.assertQuery("ignore_size", ["1"])
1170

    
1171
  def testDeactivateInstanceDisks(self):
1172
    self.rapi.AddResponse("14591")
1173
    job_id = self.client.DeactivateInstanceDisks("inst28234")
1174
    self.assertEqual(job_id, 14591)
1175
    self.assertItems(["inst28234"])
1176
    self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1177
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1178

    
1179
  def testRecreateInstanceDisks(self):
1180
    self.rapi.AddResponse("13553")
1181
    job_id = self.client.RecreateInstanceDisks("inst23153")
1182
    self.assertEqual(job_id, 13553)
1183
    self.assertItems(["inst23153"])
1184
    self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1185
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1186

    
1187
  def testGetInstanceConsole(self):
1188
    self.rapi.AddResponse("26876")
1189
    job_id = self.client.GetInstanceConsole("inst21491")
1190
    self.assertEqual(job_id, 26876)
1191
    self.assertItems(["inst21491"])
1192
    self.assertHandler(rlib2.R_2_instances_name_console)
1193
    self.assertFalse(self.rapi.GetLastHandler().queryargs)
1194
    self.assertFalse(self.rapi.GetLastRequestData())
1195

    
1196
  def testGrowInstanceDisk(self):
1197
    for idx, wait_for_sync in enumerate([None, False, True]):
1198
      amount = 128 + (512 * idx)
1199
      self.assertEqual(self.rapi.CountPending(), 0)
1200
      self.rapi.AddResponse("30783")
1201
      self.assertEqual(30783,
1202
        self.client.GrowInstanceDisk("eze8ch", idx, amount,
1203
                                     wait_for_sync=wait_for_sync))
1204
      self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1205
      self.assertItems(["eze8ch", str(idx)])
1206
      data = serializer.LoadJson(self.rapi.GetLastRequestData())
1207
      if wait_for_sync is None:
1208
        self.assertEqual(len(data), 1)
1209
        self.assert_("wait_for_sync" not in data)
1210
      else:
1211
        self.assertEqual(len(data), 2)
1212
        self.assertEqual(data["wait_for_sync"], wait_for_sync)
1213
      self.assertEqual(data["amount"], amount)
1214
      self.assertEqual(self.rapi.CountPending(), 0)
1215

    
1216
  def testGetGroupTags(self):
1217
    self.rapi.AddResponse("[]")
1218
    self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1219
    self.assertHandler(rlib2.R_2_groups_name_tags)
1220
    self.assertItems(["fooGroup"])
1221

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

    
1231
  def testDeleteGroupTags(self):
1232
    self.rapi.AddResponse("25826")
1233
    self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1234
                                                        dry_run=True))
1235
    self.assertHandler(rlib2.R_2_groups_name_tags)
1236
    self.assertItems(["foo"])
1237
    self.assertDryRun()
1238
    self.assertQuery("tag", ["awesome"])
1239

    
1240
  def testQuery(self):
1241
    for idx, what in enumerate(constants.QR_VIA_RAPI):
1242
      for idx2, filter_ in enumerate([None, ["?", "name"]]):
1243
        job_id = 11010 + (idx << 4) + (idx2 << 16)
1244
        fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1245

    
1246
        self.rapi.AddResponse(str(job_id))
1247
        self.assertEqual(self.client.Query(what, fields, filter_=filter_),
1248
                         job_id)
1249
        self.assertItems([what])
1250
        self.assertHandler(rlib2.R_2_query)
1251
        self.assertFalse(self.rapi.GetLastHandler().queryargs)
1252
        data = serializer.LoadJson(self.rapi.GetLastRequestData())
1253
        self.assertEqual(data["fields"], fields)
1254
        if filter_ is None:
1255
          self.assertTrue("filter" not in data)
1256
        else:
1257
          self.assertEqual(data["filter"], filter_)
1258
        self.assertEqual(self.rapi.CountPending(), 0)
1259

    
1260
  def testQueryFields(self):
1261
    exp_result = objects.QueryFieldsResponse(fields=[
1262
      objects.QueryFieldDefinition(name="pnode", title="PNode",
1263
                                   kind=constants.QFT_NUMBER),
1264
      objects.QueryFieldDefinition(name="other", title="Other",
1265
                                   kind=constants.QFT_BOOL),
1266
      ])
1267

    
1268
    for what in constants.QR_VIA_RAPI:
1269
      for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1270
        self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1271
        result = self.client.QueryFields(what, fields=fields)
1272
        self.assertItems([what])
1273
        self.assertHandler(rlib2.R_2_query_fields)
1274
        self.assertFalse(self.rapi.GetLastRequestData())
1275

    
1276
        queryargs = self.rapi.GetLastHandler().queryargs
1277
        if fields is None:
1278
          self.assertFalse(queryargs)
1279
        else:
1280
          self.assertEqual(queryargs, {
1281
            "fields": [",".join(fields)],
1282
            })
1283

    
1284
        self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1285
                         exp_result.ToDict())
1286

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

    
1289
  def testWaitForJobCompletionNoChange(self):
1290
    resp = serializer.DumpJson({
1291
      "status": constants.JOB_STATUS_WAITING,
1292
      })
1293

    
1294
    for retries in [1, 5, 25]:
1295
      for _ in range(retries):
1296
        self.rapi.AddResponse(resp)
1297

    
1298
      self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1299
                                                        retries=retries))
1300
      self.assertHandler(rlib2.R_2_jobs_id)
1301
      self.assertItems(["22789"])
1302

    
1303
      self.assertEqual(self.rapi.CountPending(), 0)
1304

    
1305
  def testWaitForJobCompletionAlreadyFinished(self):
1306
    self.rapi.AddResponse(serializer.DumpJson({
1307
      "status": constants.JOB_STATUS_SUCCESS,
1308
      }))
1309

    
1310
    self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1311
                                                     retries=1))
1312
    self.assertHandler(rlib2.R_2_jobs_id)
1313
    self.assertItems(["22793"])
1314

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

    
1317
  def testWaitForJobCompletionEmptyResponse(self):
1318
    self.rapi.AddResponse("{}")
1319
    self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1320
                                                     retries=10))
1321
    self.assertHandler(rlib2.R_2_jobs_id)
1322
    self.assertItems(["22793"])
1323

    
1324
    self.assertEqual(self.rapi.CountPending(), 0)
1325

    
1326
  def testWaitForJobCompletionOutOfRetries(self):
1327
    for retries in [3, 10, 21]:
1328
      for _ in range(retries):
1329
        self.rapi.AddResponse(serializer.DumpJson({
1330
          "status": constants.JOB_STATUS_RUNNING,
1331
          }))
1332

    
1333
      self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1334
                                                        retries=retries - 1))
1335
      self.assertHandler(rlib2.R_2_jobs_id)
1336
      self.assertItems(["30948"])
1337

    
1338
      self.assertEqual(self.rapi.CountPending(), 1)
1339
      self.rapi.ResetResponses()
1340

    
1341
  def testWaitForJobCompletionSuccessAndFailure(self):
1342
    for retries in [1, 4, 13]:
1343
      for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1344
                                    (True, constants.JOB_STATUS_SUCCESS)]:
1345
        for _ in range(retries):
1346
          self.rapi.AddResponse(serializer.DumpJson({
1347
            "status": constants.JOB_STATUS_RUNNING,
1348
            }))
1349

    
1350
        self.rapi.AddResponse(serializer.DumpJson({
1351
          "status": end_status,
1352
          }))
1353

    
1354
        result = self.client.WaitForJobCompletion(3187, period=None,
1355
                                                  retries=retries + 1)
1356
        self.assertEqual(result, success)
1357
        self.assertHandler(rlib2.R_2_jobs_id)
1358
        self.assertItems(["3187"])
1359

    
1360
        self.assertEqual(self.rapi.CountPending(), 0)
1361

    
1362

    
1363
class RapiTestRunner(unittest.TextTestRunner):
1364
  def run(self, *args):
1365
    global _used_handlers
1366
    assert _used_handlers is None
1367

    
1368
    _used_handlers = set()
1369
    try:
1370
      # Run actual tests
1371
      result = unittest.TextTestRunner.run(self, *args)
1372

    
1373
      diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1374
             _KNOWN_UNUSED)
1375
      if diff:
1376
        raise AssertionError("The following RAPI resources were not used by the"
1377
                             " RAPI client: %r" % utils.CommaJoin(diff))
1378
    finally:
1379
      # Reset global variable
1380
      _used_handlers = None
1381

    
1382
    return result
1383

    
1384

    
1385
if __name__ == '__main__':
1386
  client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)