Add the gnt-storage client
[ganeti-local] / test / ganeti.rapi.client_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2010, 2011 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script for unittesting the RAPI client module"""
23
24
25 import unittest
26 import warnings
27 import pycurl
28
29 from ganeti import opcodes
30 from ganeti import constants
31 from ganeti import http
32 from ganeti import serializer
33 from ganeti import utils
34 from ganeti import query
35 from ganeti import objects
36 from ganeti import rapi
37 from ganeti import errors
38
39 import ganeti.rapi.testutils
40 from ganeti.rapi import connector
41 from ganeti.rapi import rlib2
42 from ganeti.rapi import client
43
44 import testutils
45
46
47 # List of resource handlers which aren't used by the RAPI client
48 _KNOWN_UNUSED = set([
49   rlib2.R_root,
50   rlib2.R_2,
51   ])
52
53 # Global variable for collecting used handlers
54 _used_handlers = None
55
56
57 class RapiMock(object):
58   def __init__(self):
59     self._mapper = connector.Mapper()
60     self._responses = []
61     self._last_handler = None
62     self._last_req_data = None
63
64   def ResetResponses(self):
65     del self._responses[:]
66
67   def AddResponse(self, response, code=200):
68     self._responses.insert(0, (code, response))
69
70   def CountPending(self):
71     return len(self._responses)
72
73   def GetLastHandler(self):
74     return self._last_handler
75
76   def GetLastRequestData(self):
77     return self._last_req_data
78
79   def FetchResponse(self, path, method, headers, request_body):
80     self._last_req_data = request_body
81
82     try:
83       (handler_cls, items, args) = self._mapper.getController(path)
84
85       # Record handler as used
86       _used_handlers.add(handler_cls)
87
88       self._last_handler = handler_cls(items, args, None)
89       if not hasattr(self._last_handler, method.upper()):
90         raise http.HttpNotImplemented(message="Method not implemented")
91
92     except http.HttpException, ex:
93       code = ex.code
94       response = ex.message
95     else:
96       if not self._responses:
97         raise Exception("No responses")
98
99       (code, response) = self._responses.pop()
100
101     return (code, NotImplemented, response)
102
103
104 class TestConstants(unittest.TestCase):
105   def test(self):
106     self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
107     self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
108     self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
109     self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
110     self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
111     self.assertEqual(client.JOB_STATUS_WAITING, constants.JOB_STATUS_WAITING)
112     self.assertEqual(client.JOB_STATUS_CANCELING,
113                      constants.JOB_STATUS_CANCELING)
114     self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
115     self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
116     self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
117     self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
118     self.assertEqual(client.JOB_STATUS_PENDING, constants.JOBS_PENDING)
119     self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
120     self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
121
122     # Node evacuation
123     self.assertEqual(client.NODE_EVAC_PRI, constants.NODE_EVAC_PRI)
124     self.assertEqual(client.NODE_EVAC_SEC, constants.NODE_EVAC_SEC)
125     self.assertEqual(client.NODE_EVAC_ALL, constants.NODE_EVAC_ALL)
126
127     # Legacy name
128     self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITING)
129
130     # RAPI feature strings
131     self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
132     self.assertEqual(client.INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
133     self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
134     self.assertEqual(client.INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
135     self.assertEqual(client._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
136     self.assertEqual(client.NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
137     self.assertEqual(client._NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
138     self.assertEqual(client.NODE_EVAC_RES1, rlib2._NODE_EVAC_RES1)
139
140   def testErrors(self):
141     self.assertEqual(client.ECODE_ALL, errors.ECODE_ALL)
142
143     # Make sure all error codes are in both RAPI client and errors module
144     for name in filter(lambda s: (s.startswith("ECODE_") and s != "ECODE_ALL"),
145                        dir(client)):
146       value = getattr(client, name)
147       self.assertEqual(value, getattr(errors, name))
148       self.assertTrue(value in client.ECODE_ALL)
149       self.assertTrue(value in errors.ECODE_ALL)
150
151
152 class RapiMockTest(unittest.TestCase):
153   def test404(self):
154     (code, _, body) = RapiMock().FetchResponse("/foo", "GET", None, None)
155     self.assertEqual(code, 404)
156     self.assertTrue(body is None)
157
158   def test501(self):
159     (code, _, body) = RapiMock().FetchResponse("/version", "POST", None, None)
160     self.assertEqual(code, 501)
161     self.assertEqual(body, "Method not implemented")
162
163   def test200(self):
164     rapi = RapiMock()
165     rapi.AddResponse("2")
166     (code, _, response) = rapi.FetchResponse("/version", "GET", None, None)
167     self.assertEqual(200, code)
168     self.assertEqual("2", response)
169     self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
170
171
172 def _FakeNoSslPycurlVersion():
173   # Note: incomplete version tuple
174   return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
175
176
177 def _FakeFancySslPycurlVersion():
178   # Note: incomplete version tuple
179   return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
180
181
182 def _FakeOpenSslPycurlVersion():
183   # Note: incomplete version tuple
184   return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
185
186
187 def _FakeGnuTlsPycurlVersion():
188   # Note: incomplete version tuple
189   return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
190
191
192 class TestExtendedConfig(unittest.TestCase):
193   def testAuth(self):
194     cl = client.GanetiRapiClient("master.example.com",
195       username="user", password="pw",
196       curl_factory=lambda: rapi.testutils.FakeCurl(RapiMock()))
197
198     curl = cl._CreateCurl()
199     self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
200     self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
201
202   def testInvalidAuth(self):
203     # No username
204     self.assertRaises(client.Error, client.GanetiRapiClient,
205                       "master-a.example.com", password="pw")
206     # No password
207     self.assertRaises(client.Error, client.GanetiRapiClient,
208                       "master-b.example.com", username="user")
209
210   def testCertVerifyInvalidCombinations(self):
211     self.assertRaises(client.Error, client.GenericCurlConfig,
212                       use_curl_cabundle=True, cafile="cert1.pem")
213     self.assertRaises(client.Error, client.GenericCurlConfig,
214                       use_curl_cabundle=True, capath="certs/")
215     self.assertRaises(client.Error, client.GenericCurlConfig,
216                       use_curl_cabundle=True,
217                       cafile="cert1.pem", capath="certs/")
218
219   def testProxySignalVerifyHostname(self):
220     for use_gnutls in [False, True]:
221       if use_gnutls:
222         pcverfn = _FakeGnuTlsPycurlVersion
223       else:
224         pcverfn = _FakeOpenSslPycurlVersion
225
226       for proxy in ["", "http://127.0.0.1:1234"]:
227         for use_signal in [False, True]:
228           for verify_hostname in [False, True]:
229             cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
230                                              verify_hostname=verify_hostname,
231                                              _pycurl_version_fn=pcverfn)
232
233             curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
234             cl = client.GanetiRapiClient("master.example.com",
235                                          curl_config_fn=cfgfn,
236                                          curl_factory=curl_factory)
237
238             curl = cl._CreateCurl()
239             self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
240             self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
241
242             if verify_hostname:
243               self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
244             else:
245               self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
246
247   def testNoCertVerify(self):
248     cfgfn = client.GenericCurlConfig()
249
250     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
251     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
252                                  curl_factory=curl_factory)
253
254     curl = cl._CreateCurl()
255     self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
256     self.assertFalse(curl.getopt(pycurl.CAINFO))
257     self.assertFalse(curl.getopt(pycurl.CAPATH))
258
259   def testCertVerifyCurlBundle(self):
260     cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
261
262     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
263     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
264                                  curl_factory=curl_factory)
265
266     curl = cl._CreateCurl()
267     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
268     self.assertFalse(curl.getopt(pycurl.CAINFO))
269     self.assertFalse(curl.getopt(pycurl.CAPATH))
270
271   def testCertVerifyCafile(self):
272     mycert = "/tmp/some/UNUSED/cert/file.pem"
273     cfgfn = client.GenericCurlConfig(cafile=mycert)
274
275     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
276     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
277                                  curl_factory=curl_factory)
278
279     curl = cl._CreateCurl()
280     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
281     self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
282     self.assertFalse(curl.getopt(pycurl.CAPATH))
283
284   def testCertVerifyCapath(self):
285     certdir = "/tmp/some/UNUSED/cert/directory"
286     pcverfn = _FakeOpenSslPycurlVersion
287     cfgfn = client.GenericCurlConfig(capath=certdir,
288                                      _pycurl_version_fn=pcverfn)
289
290     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
291     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
292                                  curl_factory=curl_factory)
293
294     curl = cl._CreateCurl()
295     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
296     self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
297     self.assertFalse(curl.getopt(pycurl.CAINFO))
298
299   def testCertVerifyCapathGnuTls(self):
300     certdir = "/tmp/some/UNUSED/cert/directory"
301     pcverfn = _FakeGnuTlsPycurlVersion
302     cfgfn = client.GenericCurlConfig(capath=certdir,
303                                      _pycurl_version_fn=pcverfn)
304
305     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
306     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
307                                  curl_factory=curl_factory)
308
309     self.assertRaises(client.Error, cl._CreateCurl)
310
311   def testCertVerifyNoSsl(self):
312     certdir = "/tmp/some/UNUSED/cert/directory"
313     pcverfn = _FakeNoSslPycurlVersion
314     cfgfn = client.GenericCurlConfig(capath=certdir,
315                                      _pycurl_version_fn=pcverfn)
316
317     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
318     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
319                                  curl_factory=curl_factory)
320
321     self.assertRaises(client.Error, cl._CreateCurl)
322
323   def testCertVerifyFancySsl(self):
324     certdir = "/tmp/some/UNUSED/cert/directory"
325     pcverfn = _FakeFancySslPycurlVersion
326     cfgfn = client.GenericCurlConfig(capath=certdir,
327                                      _pycurl_version_fn=pcverfn)
328
329     curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
330     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
331                                  curl_factory=curl_factory)
332
333     self.assertRaises(NotImplementedError, cl._CreateCurl)
334
335   def testCertVerifyCapath(self):
336     for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
337       for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
338         cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
339                                          timeout=timeout)
340
341         curl_factory = lambda: rapi.testutils.FakeCurl(RapiMock())
342         cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
343                                      curl_factory=curl_factory)
344
345         curl = cl._CreateCurl()
346         self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
347         self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
348
349
350 class GanetiRapiClientTests(testutils.GanetiTestCase):
351   def setUp(self):
352     testutils.GanetiTestCase.setUp(self)
353
354     self.rapi = RapiMock()
355     self.curl = rapi.testutils.FakeCurl(self.rapi)
356     self.client = client.GanetiRapiClient("master.example.com",
357                                           curl_factory=lambda: self.curl)
358
359   def assertHandler(self, handler_cls):
360     self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
361
362   def assertQuery(self, key, value):
363     self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
364
365   def assertItems(self, items):
366     self.assertEqual(items, self.rapi.GetLastHandler().items)
367
368   def assertBulk(self):
369     self.assertTrue(self.rapi.GetLastHandler().useBulk())
370
371   def assertDryRun(self):
372     self.assertTrue(self.rapi.GetLastHandler().dryRun())
373
374   def assertUseForce(self):
375     self.assertTrue(self.rapi.GetLastHandler().useForce())
376
377   def testEncodeQuery(self):
378     query = [
379       ("a", None),
380       ("b", 1),
381       ("c", 2),
382       ("d", "Foo"),
383       ("e", True),
384       ]
385
386     expected = [
387       ("a", ""),
388       ("b", 1),
389       ("c", 2),
390       ("d", "Foo"),
391       ("e", 1),
392       ]
393
394     self.assertEqualValues(self.client._EncodeQuery(query),
395                            expected)
396
397     # invalid types
398     for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
399       self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
400
401   def testCurlSettings(self):
402     self.rapi.AddResponse("2")
403     self.assertEqual(2, self.client.GetVersion())
404     self.assertHandler(rlib2.R_version)
405
406     # Signals should be disabled by default
407     self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
408
409     # No auth and no proxy
410     self.assertFalse(self.curl.getopt(pycurl.USERPWD))
411     self.assert_(self.curl.getopt(pycurl.PROXY) is None)
412
413     # Content-type is required for requests
414     headers = self.curl.getopt(pycurl.HTTPHEADER)
415     self.assert_("Content-type: application/json" in headers)
416
417   def testHttpError(self):
418     self.rapi.AddResponse(None, code=404)
419     try:
420       self.client.GetJobStatus(15140)
421     except client.GanetiApiError, err:
422       self.assertEqual(err.code, 404)
423     else:
424       self.fail("Didn't raise exception")
425
426   def testGetVersion(self):
427     self.rapi.AddResponse("2")
428     self.assertEqual(2, self.client.GetVersion())
429     self.assertHandler(rlib2.R_version)
430
431   def testGetFeatures(self):
432     for features in [[], ["foo", "bar", "baz"]]:
433       self.rapi.AddResponse(serializer.DumpJson(features))
434       self.assertEqual(features, self.client.GetFeatures())
435       self.assertHandler(rlib2.R_2_features)
436
437   def testGetFeaturesNotFound(self):
438     self.rapi.AddResponse(None, code=404)
439     self.assertEqual([], self.client.GetFeatures())
440
441   def testGetOperatingSystems(self):
442     self.rapi.AddResponse("[\"beos\"]")
443     self.assertEqual(["beos"], self.client.GetOperatingSystems())
444     self.assertHandler(rlib2.R_2_os)
445
446   def testGetClusterTags(self):
447     self.rapi.AddResponse("[\"tag\"]")
448     self.assertEqual(["tag"], self.client.GetClusterTags())
449     self.assertHandler(rlib2.R_2_tags)
450
451   def testAddClusterTags(self):
452     self.rapi.AddResponse("1234")
453     self.assertEqual(1234,
454         self.client.AddClusterTags(["awesome"], dry_run=True))
455     self.assertHandler(rlib2.R_2_tags)
456     self.assertDryRun()
457     self.assertQuery("tag", ["awesome"])
458
459   def testDeleteClusterTags(self):
460     self.rapi.AddResponse("5107")
461     self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
462                                                          dry_run=True))
463     self.assertHandler(rlib2.R_2_tags)
464     self.assertDryRun()
465     self.assertQuery("tag", ["awesome"])
466
467   def testGetInfo(self):
468     self.rapi.AddResponse("{}")
469     self.assertEqual({}, self.client.GetInfo())
470     self.assertHandler(rlib2.R_2_info)
471
472   def testGetInstances(self):
473     self.rapi.AddResponse("[]")
474     self.assertEqual([], self.client.GetInstances(bulk=True))
475     self.assertHandler(rlib2.R_2_instances)
476     self.assertBulk()
477
478   def testGetInstance(self):
479     self.rapi.AddResponse("[]")
480     self.assertEqual([], self.client.GetInstance("instance"))
481     self.assertHandler(rlib2.R_2_instances_name)
482     self.assertItems(["instance"])
483
484   def testGetInstanceInfo(self):
485     self.rapi.AddResponse("21291")
486     self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
487     self.assertHandler(rlib2.R_2_instances_name_info)
488     self.assertItems(["inst3"])
489     self.assertQuery("static", None)
490
491     self.rapi.AddResponse("3428")
492     self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
493     self.assertHandler(rlib2.R_2_instances_name_info)
494     self.assertItems(["inst31"])
495     self.assertQuery("static", ["0"])
496
497     self.rapi.AddResponse("15665")
498     self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
499     self.assertHandler(rlib2.R_2_instances_name_info)
500     self.assertItems(["inst32"])
501     self.assertQuery("static", ["1"])
502
503   def testInstancesMultiAlloc(self):
504     response = {
505       constants.JOB_IDS_KEY: ["23423"],
506       opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY: ["foobar"],
507       opcodes.OpInstanceMultiAlloc.FAILED_KEY: ["foobar2"],
508       }
509     self.rapi.AddResponse(serializer.DumpJson(response))
510     insts = [self.client.InstanceAllocation("create", "foobar",
511                                             "plain", [], []),
512              self.client.InstanceAllocation("create", "foobar2",
513                                             "drbd8", [{"size": 100}], [])]
514     resp = self.client.InstancesMultiAlloc(insts)
515     self.assertEqual(resp, response)
516     self.assertHandler(rlib2.R_2_instances_multi_alloc)
517
518   def testCreateInstanceOldVersion(self):
519     # The old request format, version 0, is no longer supported
520     self.rapi.AddResponse(None, code=404)
521     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
522                       "create", "inst1.example.com", "plain", [], [])
523     self.assertEqual(self.rapi.CountPending(), 0)
524
525   def testCreateInstance(self):
526     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
527     self.rapi.AddResponse("23030")
528     job_id = self.client.CreateInstance("create", "inst1.example.com",
529                                         "plain", [], [], dry_run=True)
530     self.assertEqual(job_id, 23030)
531     self.assertHandler(rlib2.R_2_instances)
532     self.assertDryRun()
533
534     data = serializer.LoadJson(self.rapi.GetLastRequestData())
535
536     for field in ["dry_run", "beparams", "hvparams", "start"]:
537       self.assertFalse(field in data)
538
539     self.assertEqual(data["name"], "inst1.example.com")
540     self.assertEqual(data["disk_template"], "plain")
541
542   def testCreateInstance2(self):
543     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
544     self.rapi.AddResponse("24740")
545     job_id = self.client.CreateInstance("import", "inst2.example.com",
546                                         "drbd8", [{"size": 100,}],
547                                         [{}, {"bridge": "br1", }],
548                                         dry_run=False, start=True,
549                                         pnode="node1", snode="node9",
550                                         ip_check=False)
551     self.assertEqual(job_id, 24740)
552     self.assertHandler(rlib2.R_2_instances)
553
554     data = serializer.LoadJson(self.rapi.GetLastRequestData())
555     self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
556     self.assertEqual(data["name"], "inst2.example.com")
557     self.assertEqual(data["disk_template"], "drbd8")
558     self.assertEqual(data["start"], True)
559     self.assertEqual(data["ip_check"], False)
560     self.assertEqualValues(data["disks"], [{"size": 100,}])
561     self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
562
563   def testDeleteInstance(self):
564     self.rapi.AddResponse("1234")
565     self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
566     self.assertHandler(rlib2.R_2_instances_name)
567     self.assertItems(["instance"])
568     self.assertDryRun()
569
570   def testGetInstanceTags(self):
571     self.rapi.AddResponse("[]")
572     self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
573     self.assertHandler(rlib2.R_2_instances_name_tags)
574     self.assertItems(["fooinstance"])
575
576   def testAddInstanceTags(self):
577     self.rapi.AddResponse("1234")
578     self.assertEqual(1234,
579         self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
580     self.assertHandler(rlib2.R_2_instances_name_tags)
581     self.assertItems(["fooinstance"])
582     self.assertDryRun()
583     self.assertQuery("tag", ["awesome"])
584
585   def testDeleteInstanceTags(self):
586     self.rapi.AddResponse("25826")
587     self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
588                                                            dry_run=True))
589     self.assertHandler(rlib2.R_2_instances_name_tags)
590     self.assertItems(["foo"])
591     self.assertDryRun()
592     self.assertQuery("tag", ["awesome"])
593
594   def testRebootInstance(self):
595     self.rapi.AddResponse("6146")
596     job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
597                                         ignore_secondaries=True, dry_run=True)
598     self.assertEqual(6146, job_id)
599     self.assertHandler(rlib2.R_2_instances_name_reboot)
600     self.assertItems(["i-bar"])
601     self.assertDryRun()
602     self.assertQuery("type", ["hard"])
603     self.assertQuery("ignore_secondaries", ["1"])
604
605   def testShutdownInstance(self):
606     self.rapi.AddResponse("1487")
607     self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
608                                                         dry_run=True))
609     self.assertHandler(rlib2.R_2_instances_name_shutdown)
610     self.assertItems(["foo-instance"])
611     self.assertDryRun()
612
613   def testStartupInstance(self):
614     self.rapi.AddResponse("27149")
615     self.assertEqual(27149, self.client.StartupInstance("bar-instance",
616                                                         dry_run=True))
617     self.assertHandler(rlib2.R_2_instances_name_startup)
618     self.assertItems(["bar-instance"])
619     self.assertDryRun()
620
621   def testReinstallInstance(self):
622     self.rapi.AddResponse(serializer.DumpJson([]))
623     self.rapi.AddResponse("19119")
624     self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
625                                                           os="DOS",
626                                                           no_startup=True))
627     self.assertHandler(rlib2.R_2_instances_name_reinstall)
628     self.assertItems(["baz-instance"])
629     self.assertQuery("os", ["DOS"])
630     self.assertQuery("nostartup", ["1"])
631     self.assertEqual(self.rapi.CountPending(), 0)
632
633   def testReinstallInstanceNew(self):
634     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
635     self.rapi.AddResponse("25689")
636     self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
637                                                           os="Debian",
638                                                           no_startup=True))
639     self.assertHandler(rlib2.R_2_instances_name_reinstall)
640     self.assertItems(["moo-instance"])
641     data = serializer.LoadJson(self.rapi.GetLastRequestData())
642     self.assertEqual(len(data), 2)
643     self.assertEqual(data["os"], "Debian")
644     self.assertEqual(data["start"], False)
645     self.assertEqual(self.rapi.CountPending(), 0)
646
647   def testReinstallInstanceWithOsparams1(self):
648     self.rapi.AddResponse(serializer.DumpJson([]))
649     self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
650                       "doo-instance", osparams={"x": "y"})
651     self.assertEqual(self.rapi.CountPending(), 0)
652
653   def testReinstallInstanceWithOsparams2(self):
654     osparams = {
655       "Hello": "World",
656       "foo": "bar",
657       }
658     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
659     self.rapi.AddResponse("1717")
660     self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
661                                                          osparams=osparams))
662     self.assertHandler(rlib2.R_2_instances_name_reinstall)
663     self.assertItems(["zoo-instance"])
664     data = serializer.LoadJson(self.rapi.GetLastRequestData())
665     self.assertEqual(len(data), 2)
666     self.assertEqual(data["osparams"], osparams)
667     self.assertEqual(data["start"], True)
668     self.assertEqual(self.rapi.CountPending(), 0)
669
670   def testReplaceInstanceDisks(self):
671     self.rapi.AddResponse("999")
672     job_id = self.client.ReplaceInstanceDisks("instance-name",
673         disks=[0, 1], iallocator="hail")
674     self.assertEqual(999, job_id)
675     self.assertHandler(rlib2.R_2_instances_name_replace_disks)
676     self.assertItems(["instance-name"])
677     self.assertQuery("disks", ["0,1"])
678     self.assertQuery("mode", ["replace_auto"])
679     self.assertQuery("iallocator", ["hail"])
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     self.assertEqual(1000, job_id)
685     self.assertItems(["instance-bar"])
686     self.assertQuery("disks", ["1"])
687     self.assertQuery("remote_node", ["foo-node"])
688
689     self.rapi.AddResponse("5175")
690     self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
691     self.assertItems(["instance-moo"])
692     self.assertQuery("disks", None)
693
694   def testPrepareExport(self):
695     self.rapi.AddResponse("8326")
696     self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
697     self.assertHandler(rlib2.R_2_instances_name_prepare_export)
698     self.assertItems(["inst1"])
699     self.assertQuery("mode", ["local"])
700
701   def testExportInstance(self):
702     self.rapi.AddResponse("19695")
703     job_id = self.client.ExportInstance("inst2", "local", "nodeX",
704                                         shutdown=True)
705     self.assertEqual(job_id, 19695)
706     self.assertHandler(rlib2.R_2_instances_name_export)
707     self.assertItems(["inst2"])
708
709     data = serializer.LoadJson(self.rapi.GetLastRequestData())
710     self.assertEqual(data["mode"], "local")
711     self.assertEqual(data["destination"], "nodeX")
712     self.assertEqual(data["shutdown"], True)
713
714   def testMigrateInstanceDefaults(self):
715     self.rapi.AddResponse("24873")
716     job_id = self.client.MigrateInstance("inst91")
717     self.assertEqual(job_id, 24873)
718     self.assertHandler(rlib2.R_2_instances_name_migrate)
719     self.assertItems(["inst91"])
720
721     data = serializer.LoadJson(self.rapi.GetLastRequestData())
722     self.assertFalse(data)
723
724   def testMigrateInstance(self):
725     for mode in constants.HT_MIGRATION_MODES:
726       for cleanup in [False, True]:
727         self.rapi.AddResponse("31910")
728         job_id = self.client.MigrateInstance("inst289", mode=mode,
729                                              cleanup=cleanup)
730         self.assertEqual(job_id, 31910)
731         self.assertHandler(rlib2.R_2_instances_name_migrate)
732         self.assertItems(["inst289"])
733
734         data = serializer.LoadJson(self.rapi.GetLastRequestData())
735         self.assertEqual(len(data), 2)
736         self.assertEqual(data["mode"], mode)
737         self.assertEqual(data["cleanup"], cleanup)
738
739   def testFailoverInstanceDefaults(self):
740     self.rapi.AddResponse("7639")
741     job_id = self.client.FailoverInstance("inst13579")
742     self.assertEqual(job_id, 7639)
743     self.assertHandler(rlib2.R_2_instances_name_failover)
744     self.assertItems(["inst13579"])
745
746     data = serializer.LoadJson(self.rapi.GetLastRequestData())
747     self.assertFalse(data)
748
749   def testFailoverInstance(self):
750     for iallocator in ["dumb", "hail"]:
751       for ignore_consistency in [False, True]:
752         for target_node in ["node-a", "node2"]:
753           self.rapi.AddResponse("19161")
754           job_id = \
755             self.client.FailoverInstance("inst251", iallocator=iallocator,
756                                          ignore_consistency=ignore_consistency,
757                                          target_node=target_node)
758           self.assertEqual(job_id, 19161)
759           self.assertHandler(rlib2.R_2_instances_name_failover)
760           self.assertItems(["inst251"])
761
762           data = serializer.LoadJson(self.rapi.GetLastRequestData())
763           self.assertEqual(len(data), 3)
764           self.assertEqual(data["iallocator"], iallocator)
765           self.assertEqual(data["ignore_consistency"], ignore_consistency)
766           self.assertEqual(data["target_node"], target_node)
767           self.assertEqual(self.rapi.CountPending(), 0)
768
769   def testRenameInstanceDefaults(self):
770     new_name = "newnametha7euqu"
771     self.rapi.AddResponse("8791")
772     job_id = self.client.RenameInstance("inst18821", new_name)
773     self.assertEqual(job_id, 8791)
774     self.assertHandler(rlib2.R_2_instances_name_rename)
775     self.assertItems(["inst18821"])
776
777     data = serializer.LoadJson(self.rapi.GetLastRequestData())
778     self.assertEqualValues(data, {"new_name": new_name, })
779
780   def testRenameInstance(self):
781     new_name = "new-name-yiux1iin"
782     for ip_check in [False, True]:
783       for name_check in [False, True]:
784         self.rapi.AddResponse("24776")
785         job_id = self.client.RenameInstance("inst20967", new_name,
786                                              ip_check=ip_check,
787                                              name_check=name_check)
788         self.assertEqual(job_id, 24776)
789         self.assertHandler(rlib2.R_2_instances_name_rename)
790         self.assertItems(["inst20967"])
791
792         data = serializer.LoadJson(self.rapi.GetLastRequestData())
793         self.assertEqual(len(data), 3)
794         self.assertEqual(data["new_name"], new_name)
795         self.assertEqual(data["ip_check"], ip_check)
796         self.assertEqual(data["name_check"], name_check)
797
798   def testGetJobs(self):
799     self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
800                           '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
801     self.assertEqual([123, 124], self.client.GetJobs())
802     self.assertHandler(rlib2.R_2_jobs)
803
804   def testGetJobStatus(self):
805     self.rapi.AddResponse("{\"foo\": \"bar\"}")
806     self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
807     self.assertHandler(rlib2.R_2_jobs_id)
808     self.assertItems(["1234"])
809
810   def testWaitForJobChange(self):
811     fields = ["id", "summary"]
812     expected = {
813       "job_info": [123, "something"],
814       "log_entries": [],
815       }
816
817     self.rapi.AddResponse(serializer.DumpJson(expected))
818     result = self.client.WaitForJobChange(123, fields, [], -1)
819     self.assertEqualValues(expected, result)
820     self.assertHandler(rlib2.R_2_jobs_id_wait)
821     self.assertItems(["123"])
822
823   def testCancelJob(self):
824     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
825     self.assertEqual([True, "Job 123 will be canceled"],
826                      self.client.CancelJob(999, dry_run=True))
827     self.assertHandler(rlib2.R_2_jobs_id)
828     self.assertItems(["999"])
829     self.assertDryRun()
830
831   def testGetNodes(self):
832     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
833                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
834     self.assertEqual(["node1", "node2"], self.client.GetNodes())
835     self.assertHandler(rlib2.R_2_nodes)
836
837     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
838                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
839     self.assertEqual([{"id": "node1", "uri": "uri1"},
840                       {"id": "node2", "uri": "uri2"}],
841                      self.client.GetNodes(bulk=True))
842     self.assertHandler(rlib2.R_2_nodes)
843     self.assertBulk()
844
845   def testGetNode(self):
846     self.rapi.AddResponse("{}")
847     self.assertEqual({}, self.client.GetNode("node-foo"))
848     self.assertHandler(rlib2.R_2_nodes_name)
849     self.assertItems(["node-foo"])
850
851   def testEvacuateNode(self):
852     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
853     self.rapi.AddResponse("9876")
854     job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
855     self.assertEqual(9876, job_id)
856     self.assertHandler(rlib2.R_2_nodes_name_evacuate)
857     self.assertItems(["node-1"])
858     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
859                      { "remote_node": "node-2", })
860     self.assertEqual(self.rapi.CountPending(), 0)
861
862     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
863     self.rapi.AddResponse("8888")
864     job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
865                                       mode=constants.NODE_EVAC_ALL,
866                                       early_release=True)
867     self.assertEqual(8888, job_id)
868     self.assertItems(["node-3"])
869     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
870       "iallocator": "hail",
871       "mode": "all",
872       "early_release": True,
873       })
874     self.assertDryRun()
875
876     self.assertRaises(client.GanetiApiError,
877                       self.client.EvacuateNode,
878                       "node-4", iallocator="hail", remote_node="node-5")
879     self.assertEqual(self.rapi.CountPending(), 0)
880
881   def testEvacuateNodeOldResponse(self):
882     self.rapi.AddResponse(serializer.DumpJson([]))
883     self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
884                       "node-4", accept_old=False)
885     self.assertEqual(self.rapi.CountPending(), 0)
886
887     for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
888       self.rapi.AddResponse(serializer.DumpJson([]))
889       self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
890                         "node-4", accept_old=True, mode=mode)
891       self.assertEqual(self.rapi.CountPending(), 0)
892
893     self.rapi.AddResponse(serializer.DumpJson([]))
894     self.rapi.AddResponse(serializer.DumpJson("21533"))
895     result = self.client.EvacuateNode("node-3", iallocator="hail",
896                                       dry_run=True, accept_old=True,
897                                       mode=client.NODE_EVAC_SEC,
898                                       early_release=True)
899     self.assertEqual(result, "21533")
900     self.assertItems(["node-3"])
901     self.assertQuery("iallocator", ["hail"])
902     self.assertQuery("early_release", ["1"])
903     self.assertFalse(self.rapi.GetLastRequestData())
904     self.assertDryRun()
905     self.assertEqual(self.rapi.CountPending(), 0)
906
907   def testMigrateNode(self):
908     self.rapi.AddResponse(serializer.DumpJson([]))
909     self.rapi.AddResponse("1111")
910     self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
911     self.assertHandler(rlib2.R_2_nodes_name_migrate)
912     self.assertItems(["node-a"])
913     self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
914     self.assertDryRun()
915     self.assertFalse(self.rapi.GetLastRequestData())
916
917     self.rapi.AddResponse(serializer.DumpJson([]))
918     self.rapi.AddResponse("1112")
919     self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
920                                                    mode="live"))
921     self.assertHandler(rlib2.R_2_nodes_name_migrate)
922     self.assertItems(["node-a"])
923     self.assertQuery("mode", ["live"])
924     self.assertDryRun()
925     self.assertFalse(self.rapi.GetLastRequestData())
926
927     self.rapi.AddResponse(serializer.DumpJson([]))
928     self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
929                       "node-c", target_node="foonode")
930     self.assertEqual(self.rapi.CountPending(), 0)
931
932   def testMigrateNodeBodyData(self):
933     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
934     self.rapi.AddResponse("27539")
935     self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
936                                                     mode="live"))
937     self.assertHandler(rlib2.R_2_nodes_name_migrate)
938     self.assertItems(["node-a"])
939     self.assertFalse(self.rapi.GetLastHandler().queryargs)
940     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
941                      { "mode": "live", })
942
943     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
944     self.rapi.AddResponse("14219")
945     self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
946                                                     target_node="node9",
947                                                     iallocator="ial"))
948     self.assertHandler(rlib2.R_2_nodes_name_migrate)
949     self.assertItems(["node-x"])
950     self.assertDryRun()
951     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
952                      { "target_node": "node9", "iallocator": "ial", })
953
954     self.assertEqual(self.rapi.CountPending(), 0)
955
956   def testGetNodeRole(self):
957     self.rapi.AddResponse("\"master\"")
958     self.assertEqual("master", self.client.GetNodeRole("node-a"))
959     self.assertHandler(rlib2.R_2_nodes_name_role)
960     self.assertItems(["node-a"])
961
962   def testSetNodeRole(self):
963     self.rapi.AddResponse("789")
964     self.assertEqual(789,
965         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
966     self.assertHandler(rlib2.R_2_nodes_name_role)
967     self.assertItems(["node-foo"])
968     self.assertQuery("force", ["1"])
969     self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
970
971   def testPowercycleNode(self):
972     self.rapi.AddResponse("23051")
973     self.assertEqual(23051,
974         self.client.PowercycleNode("node5468", force=True))
975     self.assertHandler(rlib2.R_2_nodes_name_powercycle)
976     self.assertItems(["node5468"])
977     self.assertQuery("force", ["1"])
978     self.assertFalse(self.rapi.GetLastRequestData())
979     self.assertEqual(self.rapi.CountPending(), 0)
980
981   def testModifyNode(self):
982     self.rapi.AddResponse("3783")
983     job_id = self.client.ModifyNode("node16979.example.com", drained=True)
984     self.assertEqual(job_id, 3783)
985     self.assertHandler(rlib2.R_2_nodes_name_modify)
986     self.assertItems(["node16979.example.com"])
987     self.assertEqual(self.rapi.CountPending(), 0)
988
989   def testGetNodeStorageUnits(self):
990     self.rapi.AddResponse("42")
991     self.assertEqual(42,
992         self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
993     self.assertHandler(rlib2.R_2_nodes_name_storage)
994     self.assertItems(["node-x"])
995     self.assertQuery("storage_type", ["lvm-pv"])
996     self.assertQuery("output_fields", ["fields"])
997
998   def testModifyNodeStorageUnits(self):
999     self.rapi.AddResponse("14")
1000     self.assertEqual(14,
1001         self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
1002     self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1003     self.assertItems(["node-z"])
1004     self.assertQuery("storage_type", ["lvm-pv"])
1005     self.assertQuery("name", ["hda"])
1006     self.assertQuery("allocatable", None)
1007
1008     for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
1009       self.rapi.AddResponse("7205")
1010       job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
1011                                                   allocatable=allocatable)
1012       self.assertEqual(7205, job_id)
1013       self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1014       self.assertItems(["node-z"])
1015       self.assertQuery("storage_type", ["lvm-pv"])
1016       self.assertQuery("name", ["hda"])
1017       self.assertQuery("allocatable", [query_allocatable])
1018
1019   def testRepairNodeStorageUnits(self):
1020     self.rapi.AddResponse("99")
1021     self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1022                                                             "hda"))
1023     self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1024     self.assertItems(["node-z"])
1025     self.assertQuery("storage_type", ["lvm-pv"])
1026     self.assertQuery("name", ["hda"])
1027
1028   def testGetNodeTags(self):
1029     self.rapi.AddResponse("[\"fry\", \"bender\"]")
1030     self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1031     self.assertHandler(rlib2.R_2_nodes_name_tags)
1032     self.assertItems(["node-k"])
1033
1034   def testAddNodeTags(self):
1035     self.rapi.AddResponse("1234")
1036     self.assertEqual(1234,
1037         self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1038     self.assertHandler(rlib2.R_2_nodes_name_tags)
1039     self.assertItems(["node-v"])
1040     self.assertDryRun()
1041     self.assertQuery("tag", ["awesome"])
1042
1043   def testDeleteNodeTags(self):
1044     self.rapi.AddResponse("16861")
1045     self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1046                                                        dry_run=True))
1047     self.assertHandler(rlib2.R_2_nodes_name_tags)
1048     self.assertItems(["node-w"])
1049     self.assertDryRun()
1050     self.assertQuery("tag", ["awesome"])
1051
1052   def testGetGroups(self):
1053     groups = [{"name": "group1",
1054                "uri": "/2/groups/group1",
1055                },
1056               {"name": "group2",
1057                "uri": "/2/groups/group2",
1058                },
1059               ]
1060     self.rapi.AddResponse(serializer.DumpJson(groups))
1061     self.assertEqual(["group1", "group2"], self.client.GetGroups())
1062     self.assertHandler(rlib2.R_2_groups)
1063
1064   def testGetGroupsBulk(self):
1065     groups = [{"name": "group1",
1066                "uri": "/2/groups/group1",
1067                "node_cnt": 2,
1068                "node_list": ["gnt1.test",
1069                              "gnt2.test",
1070                              ],
1071                },
1072               {"name": "group2",
1073                "uri": "/2/groups/group2",
1074                "node_cnt": 1,
1075                "node_list": ["gnt3.test",
1076                              ],
1077                },
1078               ]
1079     self.rapi.AddResponse(serializer.DumpJson(groups))
1080
1081     self.assertEqual(groups, self.client.GetGroups(bulk=True))
1082     self.assertHandler(rlib2.R_2_groups)
1083     self.assertBulk()
1084
1085   def testGetGroup(self):
1086     group = {"ctime": None,
1087              "name": "default",
1088              }
1089     self.rapi.AddResponse(serializer.DumpJson(group))
1090     self.assertEqual({"ctime": None, "name": "default"},
1091                      self.client.GetGroup("default"))
1092     self.assertHandler(rlib2.R_2_groups_name)
1093     self.assertItems(["default"])
1094
1095   def testCreateGroup(self):
1096     self.rapi.AddResponse("12345")
1097     job_id = self.client.CreateGroup("newgroup", dry_run=True)
1098     self.assertEqual(job_id, 12345)
1099     self.assertHandler(rlib2.R_2_groups)
1100     self.assertDryRun()
1101
1102   def testDeleteGroup(self):
1103     self.rapi.AddResponse("12346")
1104     job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1105     self.assertEqual(job_id, 12346)
1106     self.assertHandler(rlib2.R_2_groups_name)
1107     self.assertDryRun()
1108
1109   def testRenameGroup(self):
1110     self.rapi.AddResponse("12347")
1111     job_id = self.client.RenameGroup("oldname", "newname")
1112     self.assertEqual(job_id, 12347)
1113     self.assertHandler(rlib2.R_2_groups_name_rename)
1114
1115   def testModifyGroup(self):
1116     self.rapi.AddResponse("12348")
1117     job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1118     self.assertEqual(job_id, 12348)
1119     self.assertHandler(rlib2.R_2_groups_name_modify)
1120
1121   def testAssignGroupNodes(self):
1122     self.rapi.AddResponse("12349")
1123     job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1124                                           force=True, dry_run=True)
1125     self.assertEqual(job_id, 12349)
1126     self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1127     self.assertDryRun()
1128     self.assertUseForce()
1129
1130   def testGetNetworksBulk(self):
1131     networks = [{"name": "network1",
1132                "uri": "/2/networks/network1",
1133                "network": "192.168.0.0/24",
1134                },
1135               {"name": "network2",
1136                "uri": "/2/networks/network2",
1137                "network": "192.168.0.0/24",
1138                },
1139               ]
1140     self.rapi.AddResponse(serializer.DumpJson(networks))
1141
1142     self.assertEqual(networks, self.client.GetNetworks(bulk=True))
1143     self.assertHandler(rlib2.R_2_networks)
1144     self.assertBulk()
1145
1146   def testGetNetwork(self):
1147     network = {"ctime": None,
1148                "name": "network1",
1149                }
1150     self.rapi.AddResponse(serializer.DumpJson(network))
1151     self.assertEqual({"ctime": None, "name": "network1"},
1152                      self.client.GetNetwork("network1"))
1153     self.assertHandler(rlib2.R_2_networks_name)
1154     self.assertItems(["network1"])
1155
1156   def testCreateNetwork(self):
1157     self.rapi.AddResponse("12345")
1158     job_id = self.client.CreateNetwork("newnetwork", network="192.168.0.0/24",
1159                                        dry_run=True)
1160     self.assertEqual(job_id, 12345)
1161     self.assertHandler(rlib2.R_2_networks)
1162     self.assertDryRun()
1163
1164   def testModifyNetwork(self):
1165     self.rapi.AddResponse("12346")
1166     job_id = self.client.ModifyNetwork("mynetwork", gateway="192.168.0.10",
1167                                      dry_run=True)
1168     self.assertEqual(job_id, 12346)
1169     self.assertHandler(rlib2.R_2_networks_name_modify)
1170
1171   def testDeleteNetwork(self):
1172     self.rapi.AddResponse("12347")
1173     job_id = self.client.DeleteNetwork("newnetwork", dry_run=True)
1174     self.assertEqual(job_id, 12347)
1175     self.assertHandler(rlib2.R_2_networks_name)
1176     self.assertDryRun()
1177
1178   def testConnectNetwork(self):
1179     self.rapi.AddResponse("12348")
1180     job_id = self.client.ConnectNetwork("mynetwork", "default",
1181                                         "bridged", "br0", dry_run=True)
1182     self.assertEqual(job_id, 12348)
1183     self.assertHandler(rlib2.R_2_networks_name_connect)
1184     self.assertDryRun()
1185
1186   def testDisconnectNetwork(self):
1187     self.rapi.AddResponse("12349")
1188     job_id = self.client.DisconnectNetwork("mynetwork", "default", dry_run=True)
1189     self.assertEqual(job_id, 12349)
1190     self.assertHandler(rlib2.R_2_networks_name_disconnect)
1191     self.assertDryRun()
1192
1193   def testGetNetworkTags(self):
1194     self.rapi.AddResponse("[]")
1195     self.assertEqual([], self.client.GetNetworkTags("fooNetwork"))
1196     self.assertHandler(rlib2.R_2_networks_name_tags)
1197     self.assertItems(["fooNetwork"])
1198
1199   def testAddNetworkTags(self):
1200     self.rapi.AddResponse("1234")
1201     self.assertEqual(1234,
1202         self.client.AddNetworkTags("fooNetwork", ["awesome"], dry_run=True))
1203     self.assertHandler(rlib2.R_2_networks_name_tags)
1204     self.assertItems(["fooNetwork"])
1205     self.assertDryRun()
1206     self.assertQuery("tag", ["awesome"])
1207
1208   def testDeleteNetworkTags(self):
1209     self.rapi.AddResponse("25826")
1210     self.assertEqual(25826, self.client.DeleteNetworkTags("foo", ["awesome"],
1211                                                           dry_run=True))
1212     self.assertHandler(rlib2.R_2_networks_name_tags)
1213     self.assertItems(["foo"])
1214     self.assertDryRun()
1215     self.assertQuery("tag", ["awesome"])
1216
1217   def testModifyInstance(self):
1218     self.rapi.AddResponse("23681")
1219     job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1220     self.assertEqual(job_id, 23681)
1221     self.assertItems(["inst7210"])
1222     self.assertHandler(rlib2.R_2_instances_name_modify)
1223     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1224                      { "os_name": "linux", })
1225
1226   def testModifyCluster(self):
1227     for mnh in [None, False, True]:
1228       self.rapi.AddResponse("14470")
1229       self.assertEqual(14470,
1230         self.client.ModifyCluster(maintain_node_health=mnh))
1231       self.assertHandler(rlib2.R_2_cluster_modify)
1232       self.assertItems([])
1233       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1234       self.assertEqual(len(data), 1)
1235       self.assertEqual(data["maintain_node_health"], mnh)
1236       self.assertEqual(self.rapi.CountPending(), 0)
1237
1238   def testRedistributeConfig(self):
1239     self.rapi.AddResponse("3364")
1240     job_id = self.client.RedistributeConfig()
1241     self.assertEqual(job_id, 3364)
1242     self.assertItems([])
1243     self.assertHandler(rlib2.R_2_redist_config)
1244
1245   def testActivateInstanceDisks(self):
1246     self.rapi.AddResponse("23547")
1247     job_id = self.client.ActivateInstanceDisks("inst28204")
1248     self.assertEqual(job_id, 23547)
1249     self.assertItems(["inst28204"])
1250     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1251     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1252
1253   def testActivateInstanceDisksIgnoreSize(self):
1254     self.rapi.AddResponse("11044")
1255     job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1256     self.assertEqual(job_id, 11044)
1257     self.assertItems(["inst28204"])
1258     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1259     self.assertQuery("ignore_size", ["1"])
1260
1261   def testDeactivateInstanceDisks(self):
1262     self.rapi.AddResponse("14591")
1263     job_id = self.client.DeactivateInstanceDisks("inst28234")
1264     self.assertEqual(job_id, 14591)
1265     self.assertItems(["inst28234"])
1266     self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1267     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1268
1269   def testRecreateInstanceDisks(self):
1270     self.rapi.AddResponse("13553")
1271     job_id = self.client.RecreateInstanceDisks("inst23153")
1272     self.assertEqual(job_id, 13553)
1273     self.assertItems(["inst23153"])
1274     self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1275     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1276
1277   def testGetInstanceConsole(self):
1278     self.rapi.AddResponse("26876")
1279     job_id = self.client.GetInstanceConsole("inst21491")
1280     self.assertEqual(job_id, 26876)
1281     self.assertItems(["inst21491"])
1282     self.assertHandler(rlib2.R_2_instances_name_console)
1283     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1284     self.assertFalse(self.rapi.GetLastRequestData())
1285
1286   def testGrowInstanceDisk(self):
1287     for idx, wait_for_sync in enumerate([None, False, True]):
1288       amount = 128 + (512 * idx)
1289       self.assertEqual(self.rapi.CountPending(), 0)
1290       self.rapi.AddResponse("30783")
1291       self.assertEqual(30783,
1292         self.client.GrowInstanceDisk("eze8ch", idx, amount,
1293                                      wait_for_sync=wait_for_sync))
1294       self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1295       self.assertItems(["eze8ch", str(idx)])
1296       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1297       if wait_for_sync is None:
1298         self.assertEqual(len(data), 1)
1299         self.assert_("wait_for_sync" not in data)
1300       else:
1301         self.assertEqual(len(data), 2)
1302         self.assertEqual(data["wait_for_sync"], wait_for_sync)
1303       self.assertEqual(data["amount"], amount)
1304       self.assertEqual(self.rapi.CountPending(), 0)
1305
1306   def testGetGroupTags(self):
1307     self.rapi.AddResponse("[]")
1308     self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1309     self.assertHandler(rlib2.R_2_groups_name_tags)
1310     self.assertItems(["fooGroup"])
1311
1312   def testAddGroupTags(self):
1313     self.rapi.AddResponse("1234")
1314     self.assertEqual(1234,
1315         self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1316     self.assertHandler(rlib2.R_2_groups_name_tags)
1317     self.assertItems(["fooGroup"])
1318     self.assertDryRun()
1319     self.assertQuery("tag", ["awesome"])
1320
1321   def testDeleteGroupTags(self):
1322     self.rapi.AddResponse("25826")
1323     self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1324                                                         dry_run=True))
1325     self.assertHandler(rlib2.R_2_groups_name_tags)
1326     self.assertItems(["foo"])
1327     self.assertDryRun()
1328     self.assertQuery("tag", ["awesome"])
1329
1330   def testQuery(self):
1331     for idx, what in enumerate(constants.QR_VIA_RAPI):
1332       for idx2, qfilter in enumerate([None, ["?", "name"]]):
1333         job_id = 11010 + (idx << 4) + (idx2 << 16)
1334         fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1335
1336         self.rapi.AddResponse(str(job_id))
1337         self.assertEqual(self.client.Query(what, fields, qfilter=qfilter),
1338                          job_id)
1339         self.assertItems([what])
1340         self.assertHandler(rlib2.R_2_query)
1341         self.assertFalse(self.rapi.GetLastHandler().queryargs)
1342         data = serializer.LoadJson(self.rapi.GetLastRequestData())
1343         self.assertEqual(data["fields"], fields)
1344         if qfilter is None:
1345           self.assertTrue("qfilter" not in data)
1346         else:
1347           self.assertEqual(data["qfilter"], qfilter)
1348         self.assertEqual(self.rapi.CountPending(), 0)
1349
1350   def testQueryFields(self):
1351     exp_result = objects.QueryFieldsResponse(fields=[
1352       objects.QueryFieldDefinition(name="pnode", title="PNode",
1353                                    kind=constants.QFT_NUMBER),
1354       objects.QueryFieldDefinition(name="other", title="Other",
1355                                    kind=constants.QFT_BOOL),
1356       ])
1357
1358     for what in constants.QR_VIA_RAPI:
1359       for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1360         self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1361         result = self.client.QueryFields(what, fields=fields)
1362         self.assertItems([what])
1363         self.assertHandler(rlib2.R_2_query_fields)
1364         self.assertFalse(self.rapi.GetLastRequestData())
1365
1366         queryargs = self.rapi.GetLastHandler().queryargs
1367         if fields is None:
1368           self.assertFalse(queryargs)
1369         else:
1370           self.assertEqual(queryargs, {
1371             "fields": [",".join(fields)],
1372             })
1373
1374         self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1375                          exp_result.ToDict())
1376
1377         self.assertEqual(self.rapi.CountPending(), 0)
1378
1379   def testWaitForJobCompletionNoChange(self):
1380     resp = serializer.DumpJson({
1381       "status": constants.JOB_STATUS_WAITING,
1382       })
1383
1384     for retries in [1, 5, 25]:
1385       for _ in range(retries):
1386         self.rapi.AddResponse(resp)
1387
1388       self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1389                                                         retries=retries))
1390       self.assertHandler(rlib2.R_2_jobs_id)
1391       self.assertItems(["22789"])
1392
1393       self.assertEqual(self.rapi.CountPending(), 0)
1394
1395   def testWaitForJobCompletionAlreadyFinished(self):
1396     self.rapi.AddResponse(serializer.DumpJson({
1397       "status": constants.JOB_STATUS_SUCCESS,
1398       }))
1399
1400     self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1401                                                      retries=1))
1402     self.assertHandler(rlib2.R_2_jobs_id)
1403     self.assertItems(["22793"])
1404
1405     self.assertEqual(self.rapi.CountPending(), 0)
1406
1407   def testWaitForJobCompletionEmptyResponse(self):
1408     self.rapi.AddResponse("{}")
1409     self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1410                                                      retries=10))
1411     self.assertHandler(rlib2.R_2_jobs_id)
1412     self.assertItems(["22793"])
1413
1414     self.assertEqual(self.rapi.CountPending(), 0)
1415
1416   def testWaitForJobCompletionOutOfRetries(self):
1417     for retries in [3, 10, 21]:
1418       for _ in range(retries):
1419         self.rapi.AddResponse(serializer.DumpJson({
1420           "status": constants.JOB_STATUS_RUNNING,
1421           }))
1422
1423       self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1424                                                         retries=retries - 1))
1425       self.assertHandler(rlib2.R_2_jobs_id)
1426       self.assertItems(["30948"])
1427
1428       self.assertEqual(self.rapi.CountPending(), 1)
1429       self.rapi.ResetResponses()
1430
1431   def testWaitForJobCompletionSuccessAndFailure(self):
1432     for retries in [1, 4, 13]:
1433       for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1434                                     (True, constants.JOB_STATUS_SUCCESS)]:
1435         for _ in range(retries):
1436           self.rapi.AddResponse(serializer.DumpJson({
1437             "status": constants.JOB_STATUS_RUNNING,
1438             }))
1439
1440         self.rapi.AddResponse(serializer.DumpJson({
1441           "status": end_status,
1442           }))
1443
1444         result = self.client.WaitForJobCompletion(3187, period=None,
1445                                                   retries=retries + 1)
1446         self.assertEqual(result, success)
1447         self.assertHandler(rlib2.R_2_jobs_id)
1448         self.assertItems(["3187"])
1449
1450         self.assertEqual(self.rapi.CountPending(), 0)
1451
1452
1453 class RapiTestRunner(unittest.TextTestRunner):
1454   def run(self, *args):
1455     global _used_handlers
1456     assert _used_handlers is None
1457
1458     _used_handlers = set()
1459     try:
1460       # Run actual tests
1461       result = unittest.TextTestRunner.run(self, *args)
1462
1463       diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1464              _KNOWN_UNUSED)
1465       if diff:
1466         raise AssertionError("The following RAPI resources were not used by the"
1467                              " RAPI client: %r" % utils.CommaJoin(diff))
1468     finally:
1469       # Reset global variable
1470       _used_handlers = None
1471
1472     return result
1473
1474
1475 if __name__ == "__main__":
1476   client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)