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