Revert unintentional change of daemon log file names
[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 constants
30 from ganeti import http
31 from ganeti import serializer
32 from ganeti import utils
33 from ganeti import query
34 from ganeti import objects
35 from ganeti import rapi
36
37 import ganeti.rapi.testutils
38 from ganeti.rapi import connector
39 from ganeti.rapi import rlib2
40 from ganeti.rapi import client
41
42 import testutils
43
44
45 # List of resource handlers which aren't used by the RAPI client
46 _KNOWN_UNUSED = set([
47   rlib2.R_root,
48   rlib2.R_2,
49   rlib2.R_2_instances_multi_alloc,
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 testCreateInstanceOldVersion(self):
485     # The old request format, version 0, is no longer supported
486     self.rapi.AddResponse(None, code=404)
487     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
488                       "create", "inst1.example.com", "plain", [], [])
489     self.assertEqual(self.rapi.CountPending(), 0)
490
491   def testCreateInstance(self):
492     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
493     self.rapi.AddResponse("23030")
494     job_id = self.client.CreateInstance("create", "inst1.example.com",
495                                         "plain", [], [], dry_run=True)
496     self.assertEqual(job_id, 23030)
497     self.assertHandler(rlib2.R_2_instances)
498     self.assertDryRun()
499
500     data = serializer.LoadJson(self.rapi.GetLastRequestData())
501
502     for field in ["dry_run", "beparams", "hvparams", "start"]:
503       self.assertFalse(field in data)
504
505     self.assertEqual(data["name"], "inst1.example.com")
506     self.assertEqual(data["disk_template"], "plain")
507
508   def testCreateInstance2(self):
509     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
510     self.rapi.AddResponse("24740")
511     job_id = self.client.CreateInstance("import", "inst2.example.com",
512                                         "drbd8", [{"size": 100,}],
513                                         [{}, {"bridge": "br1", }],
514                                         dry_run=False, start=True,
515                                         pnode="node1", snode="node9",
516                                         ip_check=False)
517     self.assertEqual(job_id, 24740)
518     self.assertHandler(rlib2.R_2_instances)
519
520     data = serializer.LoadJson(self.rapi.GetLastRequestData())
521     self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
522     self.assertEqual(data["name"], "inst2.example.com")
523     self.assertEqual(data["disk_template"], "drbd8")
524     self.assertEqual(data["start"], True)
525     self.assertEqual(data["ip_check"], False)
526     self.assertEqualValues(data["disks"], [{"size": 100,}])
527     self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
528
529   def testDeleteInstance(self):
530     self.rapi.AddResponse("1234")
531     self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
532     self.assertHandler(rlib2.R_2_instances_name)
533     self.assertItems(["instance"])
534     self.assertDryRun()
535
536   def testGetInstanceTags(self):
537     self.rapi.AddResponse("[]")
538     self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
539     self.assertHandler(rlib2.R_2_instances_name_tags)
540     self.assertItems(["fooinstance"])
541
542   def testAddInstanceTags(self):
543     self.rapi.AddResponse("1234")
544     self.assertEqual(1234,
545         self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
546     self.assertHandler(rlib2.R_2_instances_name_tags)
547     self.assertItems(["fooinstance"])
548     self.assertDryRun()
549     self.assertQuery("tag", ["awesome"])
550
551   def testDeleteInstanceTags(self):
552     self.rapi.AddResponse("25826")
553     self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
554                                                            dry_run=True))
555     self.assertHandler(rlib2.R_2_instances_name_tags)
556     self.assertItems(["foo"])
557     self.assertDryRun()
558     self.assertQuery("tag", ["awesome"])
559
560   def testRebootInstance(self):
561     self.rapi.AddResponse("6146")
562     job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
563                                         ignore_secondaries=True, dry_run=True)
564     self.assertEqual(6146, job_id)
565     self.assertHandler(rlib2.R_2_instances_name_reboot)
566     self.assertItems(["i-bar"])
567     self.assertDryRun()
568     self.assertQuery("type", ["hard"])
569     self.assertQuery("ignore_secondaries", ["1"])
570
571   def testShutdownInstance(self):
572     self.rapi.AddResponse("1487")
573     self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
574                                                         dry_run=True))
575     self.assertHandler(rlib2.R_2_instances_name_shutdown)
576     self.assertItems(["foo-instance"])
577     self.assertDryRun()
578
579   def testStartupInstance(self):
580     self.rapi.AddResponse("27149")
581     self.assertEqual(27149, self.client.StartupInstance("bar-instance",
582                                                         dry_run=True))
583     self.assertHandler(rlib2.R_2_instances_name_startup)
584     self.assertItems(["bar-instance"])
585     self.assertDryRun()
586
587   def testReinstallInstance(self):
588     self.rapi.AddResponse(serializer.DumpJson([]))
589     self.rapi.AddResponse("19119")
590     self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
591                                                           os="DOS",
592                                                           no_startup=True))
593     self.assertHandler(rlib2.R_2_instances_name_reinstall)
594     self.assertItems(["baz-instance"])
595     self.assertQuery("os", ["DOS"])
596     self.assertQuery("nostartup", ["1"])
597     self.assertEqual(self.rapi.CountPending(), 0)
598
599   def testReinstallInstanceNew(self):
600     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
601     self.rapi.AddResponse("25689")
602     self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
603                                                           os="Debian",
604                                                           no_startup=True))
605     self.assertHandler(rlib2.R_2_instances_name_reinstall)
606     self.assertItems(["moo-instance"])
607     data = serializer.LoadJson(self.rapi.GetLastRequestData())
608     self.assertEqual(len(data), 2)
609     self.assertEqual(data["os"], "Debian")
610     self.assertEqual(data["start"], False)
611     self.assertEqual(self.rapi.CountPending(), 0)
612
613   def testReinstallInstanceWithOsparams1(self):
614     self.rapi.AddResponse(serializer.DumpJson([]))
615     self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
616                       "doo-instance", osparams={"x": "y"})
617     self.assertEqual(self.rapi.CountPending(), 0)
618
619   def testReinstallInstanceWithOsparams2(self):
620     osparams = {
621       "Hello": "World",
622       "foo": "bar",
623       }
624     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
625     self.rapi.AddResponse("1717")
626     self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
627                                                          osparams=osparams))
628     self.assertHandler(rlib2.R_2_instances_name_reinstall)
629     self.assertItems(["zoo-instance"])
630     data = serializer.LoadJson(self.rapi.GetLastRequestData())
631     self.assertEqual(len(data), 2)
632     self.assertEqual(data["osparams"], osparams)
633     self.assertEqual(data["start"], True)
634     self.assertEqual(self.rapi.CountPending(), 0)
635
636   def testReplaceInstanceDisks(self):
637     self.rapi.AddResponse("999")
638     job_id = self.client.ReplaceInstanceDisks("instance-name",
639         disks=[0, 1], iallocator="hail")
640     self.assertEqual(999, job_id)
641     self.assertHandler(rlib2.R_2_instances_name_replace_disks)
642     self.assertItems(["instance-name"])
643     self.assertQuery("disks", ["0,1"])
644     self.assertQuery("mode", ["replace_auto"])
645     self.assertQuery("iallocator", ["hail"])
646
647     self.rapi.AddResponse("1000")
648     job_id = self.client.ReplaceInstanceDisks("instance-bar",
649         disks=[1], mode="replace_on_secondary", remote_node="foo-node")
650     self.assertEqual(1000, job_id)
651     self.assertItems(["instance-bar"])
652     self.assertQuery("disks", ["1"])
653     self.assertQuery("remote_node", ["foo-node"])
654
655     self.rapi.AddResponse("5175")
656     self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
657     self.assertItems(["instance-moo"])
658     self.assertQuery("disks", None)
659
660   def testPrepareExport(self):
661     self.rapi.AddResponse("8326")
662     self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
663     self.assertHandler(rlib2.R_2_instances_name_prepare_export)
664     self.assertItems(["inst1"])
665     self.assertQuery("mode", ["local"])
666
667   def testExportInstance(self):
668     self.rapi.AddResponse("19695")
669     job_id = self.client.ExportInstance("inst2", "local", "nodeX",
670                                         shutdown=True)
671     self.assertEqual(job_id, 19695)
672     self.assertHandler(rlib2.R_2_instances_name_export)
673     self.assertItems(["inst2"])
674
675     data = serializer.LoadJson(self.rapi.GetLastRequestData())
676     self.assertEqual(data["mode"], "local")
677     self.assertEqual(data["destination"], "nodeX")
678     self.assertEqual(data["shutdown"], True)
679
680   def testMigrateInstanceDefaults(self):
681     self.rapi.AddResponse("24873")
682     job_id = self.client.MigrateInstance("inst91")
683     self.assertEqual(job_id, 24873)
684     self.assertHandler(rlib2.R_2_instances_name_migrate)
685     self.assertItems(["inst91"])
686
687     data = serializer.LoadJson(self.rapi.GetLastRequestData())
688     self.assertFalse(data)
689
690   def testMigrateInstance(self):
691     for mode in constants.HT_MIGRATION_MODES:
692       for cleanup in [False, True]:
693         self.rapi.AddResponse("31910")
694         job_id = self.client.MigrateInstance("inst289", mode=mode,
695                                              cleanup=cleanup)
696         self.assertEqual(job_id, 31910)
697         self.assertHandler(rlib2.R_2_instances_name_migrate)
698         self.assertItems(["inst289"])
699
700         data = serializer.LoadJson(self.rapi.GetLastRequestData())
701         self.assertEqual(len(data), 2)
702         self.assertEqual(data["mode"], mode)
703         self.assertEqual(data["cleanup"], cleanup)
704
705   def testFailoverInstanceDefaults(self):
706     self.rapi.AddResponse("7639")
707     job_id = self.client.FailoverInstance("inst13579")
708     self.assertEqual(job_id, 7639)
709     self.assertHandler(rlib2.R_2_instances_name_failover)
710     self.assertItems(["inst13579"])
711
712     data = serializer.LoadJson(self.rapi.GetLastRequestData())
713     self.assertFalse(data)
714
715   def testFailoverInstance(self):
716     for iallocator in ["dumb", "hail"]:
717       for ignore_consistency in [False, True]:
718         for target_node in ["node-a", "node2"]:
719           self.rapi.AddResponse("19161")
720           job_id = \
721             self.client.FailoverInstance("inst251", iallocator=iallocator,
722                                          ignore_consistency=ignore_consistency,
723                                          target_node=target_node)
724           self.assertEqual(job_id, 19161)
725           self.assertHandler(rlib2.R_2_instances_name_failover)
726           self.assertItems(["inst251"])
727
728           data = serializer.LoadJson(self.rapi.GetLastRequestData())
729           self.assertEqual(len(data), 3)
730           self.assertEqual(data["iallocator"], iallocator)
731           self.assertEqual(data["ignore_consistency"], ignore_consistency)
732           self.assertEqual(data["target_node"], target_node)
733           self.assertEqual(self.rapi.CountPending(), 0)
734
735   def testRenameInstanceDefaults(self):
736     new_name = "newnametha7euqu"
737     self.rapi.AddResponse("8791")
738     job_id = self.client.RenameInstance("inst18821", new_name)
739     self.assertEqual(job_id, 8791)
740     self.assertHandler(rlib2.R_2_instances_name_rename)
741     self.assertItems(["inst18821"])
742
743     data = serializer.LoadJson(self.rapi.GetLastRequestData())
744     self.assertEqualValues(data, {"new_name": new_name, })
745
746   def testRenameInstance(self):
747     new_name = "new-name-yiux1iin"
748     for ip_check in [False, True]:
749       for name_check in [False, True]:
750         self.rapi.AddResponse("24776")
751         job_id = self.client.RenameInstance("inst20967", new_name,
752                                              ip_check=ip_check,
753                                              name_check=name_check)
754         self.assertEqual(job_id, 24776)
755         self.assertHandler(rlib2.R_2_instances_name_rename)
756         self.assertItems(["inst20967"])
757
758         data = serializer.LoadJson(self.rapi.GetLastRequestData())
759         self.assertEqual(len(data), 3)
760         self.assertEqual(data["new_name"], new_name)
761         self.assertEqual(data["ip_check"], ip_check)
762         self.assertEqual(data["name_check"], name_check)
763
764   def testGetJobs(self):
765     self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
766                           '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
767     self.assertEqual([123, 124], self.client.GetJobs())
768     self.assertHandler(rlib2.R_2_jobs)
769
770   def testGetJobStatus(self):
771     self.rapi.AddResponse("{\"foo\": \"bar\"}")
772     self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
773     self.assertHandler(rlib2.R_2_jobs_id)
774     self.assertItems(["1234"])
775
776   def testWaitForJobChange(self):
777     fields = ["id", "summary"]
778     expected = {
779       "job_info": [123, "something"],
780       "log_entries": [],
781       }
782
783     self.rapi.AddResponse(serializer.DumpJson(expected))
784     result = self.client.WaitForJobChange(123, fields, [], -1)
785     self.assertEqualValues(expected, result)
786     self.assertHandler(rlib2.R_2_jobs_id_wait)
787     self.assertItems(["123"])
788
789   def testCancelJob(self):
790     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
791     self.assertEqual([True, "Job 123 will be canceled"],
792                      self.client.CancelJob(999, dry_run=True))
793     self.assertHandler(rlib2.R_2_jobs_id)
794     self.assertItems(["999"])
795     self.assertDryRun()
796
797   def testGetNodes(self):
798     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
799                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
800     self.assertEqual(["node1", "node2"], self.client.GetNodes())
801     self.assertHandler(rlib2.R_2_nodes)
802
803     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
804                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
805     self.assertEqual([{"id": "node1", "uri": "uri1"},
806                       {"id": "node2", "uri": "uri2"}],
807                      self.client.GetNodes(bulk=True))
808     self.assertHandler(rlib2.R_2_nodes)
809     self.assertBulk()
810
811   def testGetNode(self):
812     self.rapi.AddResponse("{}")
813     self.assertEqual({}, self.client.GetNode("node-foo"))
814     self.assertHandler(rlib2.R_2_nodes_name)
815     self.assertItems(["node-foo"])
816
817   def testEvacuateNode(self):
818     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
819     self.rapi.AddResponse("9876")
820     job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
821     self.assertEqual(9876, job_id)
822     self.assertHandler(rlib2.R_2_nodes_name_evacuate)
823     self.assertItems(["node-1"])
824     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
825                      { "remote_node": "node-2", })
826     self.assertEqual(self.rapi.CountPending(), 0)
827
828     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
829     self.rapi.AddResponse("8888")
830     job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
831                                       mode=constants.NODE_EVAC_ALL,
832                                       early_release=True)
833     self.assertEqual(8888, job_id)
834     self.assertItems(["node-3"])
835     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
836       "iallocator": "hail",
837       "mode": "all",
838       "early_release": True,
839       })
840     self.assertDryRun()
841
842     self.assertRaises(client.GanetiApiError,
843                       self.client.EvacuateNode,
844                       "node-4", iallocator="hail", remote_node="node-5")
845     self.assertEqual(self.rapi.CountPending(), 0)
846
847   def testEvacuateNodeOldResponse(self):
848     self.rapi.AddResponse(serializer.DumpJson([]))
849     self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
850                       "node-4", accept_old=False)
851     self.assertEqual(self.rapi.CountPending(), 0)
852
853     for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
854       self.rapi.AddResponse(serializer.DumpJson([]))
855       self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
856                         "node-4", accept_old=True, mode=mode)
857       self.assertEqual(self.rapi.CountPending(), 0)
858
859     self.rapi.AddResponse(serializer.DumpJson([]))
860     self.rapi.AddResponse(serializer.DumpJson("21533"))
861     result = self.client.EvacuateNode("node-3", iallocator="hail",
862                                       dry_run=True, accept_old=True,
863                                       mode=client.NODE_EVAC_SEC,
864                                       early_release=True)
865     self.assertEqual(result, "21533")
866     self.assertItems(["node-3"])
867     self.assertQuery("iallocator", ["hail"])
868     self.assertQuery("early_release", ["1"])
869     self.assertFalse(self.rapi.GetLastRequestData())
870     self.assertDryRun()
871     self.assertEqual(self.rapi.CountPending(), 0)
872
873   def testMigrateNode(self):
874     self.rapi.AddResponse(serializer.DumpJson([]))
875     self.rapi.AddResponse("1111")
876     self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
877     self.assertHandler(rlib2.R_2_nodes_name_migrate)
878     self.assertItems(["node-a"])
879     self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
880     self.assertDryRun()
881     self.assertFalse(self.rapi.GetLastRequestData())
882
883     self.rapi.AddResponse(serializer.DumpJson([]))
884     self.rapi.AddResponse("1112")
885     self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
886                                                    mode="live"))
887     self.assertHandler(rlib2.R_2_nodes_name_migrate)
888     self.assertItems(["node-a"])
889     self.assertQuery("mode", ["live"])
890     self.assertDryRun()
891     self.assertFalse(self.rapi.GetLastRequestData())
892
893     self.rapi.AddResponse(serializer.DumpJson([]))
894     self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
895                       "node-c", target_node="foonode")
896     self.assertEqual(self.rapi.CountPending(), 0)
897
898   def testMigrateNodeBodyData(self):
899     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
900     self.rapi.AddResponse("27539")
901     self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
902                                                     mode="live"))
903     self.assertHandler(rlib2.R_2_nodes_name_migrate)
904     self.assertItems(["node-a"])
905     self.assertFalse(self.rapi.GetLastHandler().queryargs)
906     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
907                      { "mode": "live", })
908
909     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
910     self.rapi.AddResponse("14219")
911     self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
912                                                     target_node="node9",
913                                                     iallocator="ial"))
914     self.assertHandler(rlib2.R_2_nodes_name_migrate)
915     self.assertItems(["node-x"])
916     self.assertDryRun()
917     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
918                      { "target_node": "node9", "iallocator": "ial", })
919
920     self.assertEqual(self.rapi.CountPending(), 0)
921
922   def testGetNodeRole(self):
923     self.rapi.AddResponse("\"master\"")
924     self.assertEqual("master", self.client.GetNodeRole("node-a"))
925     self.assertHandler(rlib2.R_2_nodes_name_role)
926     self.assertItems(["node-a"])
927
928   def testSetNodeRole(self):
929     self.rapi.AddResponse("789")
930     self.assertEqual(789,
931         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
932     self.assertHandler(rlib2.R_2_nodes_name_role)
933     self.assertItems(["node-foo"])
934     self.assertQuery("force", ["1"])
935     self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
936
937   def testPowercycleNode(self):
938     self.rapi.AddResponse("23051")
939     self.assertEqual(23051,
940         self.client.PowercycleNode("node5468", force=True))
941     self.assertHandler(rlib2.R_2_nodes_name_powercycle)
942     self.assertItems(["node5468"])
943     self.assertQuery("force", ["1"])
944     self.assertFalse(self.rapi.GetLastRequestData())
945     self.assertEqual(self.rapi.CountPending(), 0)
946
947   def testModifyNode(self):
948     self.rapi.AddResponse("3783")
949     job_id = self.client.ModifyNode("node16979.example.com", drained=True)
950     self.assertEqual(job_id, 3783)
951     self.assertHandler(rlib2.R_2_nodes_name_modify)
952     self.assertItems(["node16979.example.com"])
953     self.assertEqual(self.rapi.CountPending(), 0)
954
955   def testGetNodeStorageUnits(self):
956     self.rapi.AddResponse("42")
957     self.assertEqual(42,
958         self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
959     self.assertHandler(rlib2.R_2_nodes_name_storage)
960     self.assertItems(["node-x"])
961     self.assertQuery("storage_type", ["lvm-pv"])
962     self.assertQuery("output_fields", ["fields"])
963
964   def testModifyNodeStorageUnits(self):
965     self.rapi.AddResponse("14")
966     self.assertEqual(14,
967         self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
968     self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
969     self.assertItems(["node-z"])
970     self.assertQuery("storage_type", ["lvm-pv"])
971     self.assertQuery("name", ["hda"])
972     self.assertQuery("allocatable", None)
973
974     for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
975       self.rapi.AddResponse("7205")
976       job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
977                                                   allocatable=allocatable)
978       self.assertEqual(7205, job_id)
979       self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
980       self.assertItems(["node-z"])
981       self.assertQuery("storage_type", ["lvm-pv"])
982       self.assertQuery("name", ["hda"])
983       self.assertQuery("allocatable", [query_allocatable])
984
985   def testRepairNodeStorageUnits(self):
986     self.rapi.AddResponse("99")
987     self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
988                                                             "hda"))
989     self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
990     self.assertItems(["node-z"])
991     self.assertQuery("storage_type", ["lvm-pv"])
992     self.assertQuery("name", ["hda"])
993
994   def testGetNodeTags(self):
995     self.rapi.AddResponse("[\"fry\", \"bender\"]")
996     self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
997     self.assertHandler(rlib2.R_2_nodes_name_tags)
998     self.assertItems(["node-k"])
999
1000   def testAddNodeTags(self):
1001     self.rapi.AddResponse("1234")
1002     self.assertEqual(1234,
1003         self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1004     self.assertHandler(rlib2.R_2_nodes_name_tags)
1005     self.assertItems(["node-v"])
1006     self.assertDryRun()
1007     self.assertQuery("tag", ["awesome"])
1008
1009   def testDeleteNodeTags(self):
1010     self.rapi.AddResponse("16861")
1011     self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1012                                                        dry_run=True))
1013     self.assertHandler(rlib2.R_2_nodes_name_tags)
1014     self.assertItems(["node-w"])
1015     self.assertDryRun()
1016     self.assertQuery("tag", ["awesome"])
1017
1018   def testGetGroups(self):
1019     groups = [{"name": "group1",
1020                "uri": "/2/groups/group1",
1021                },
1022               {"name": "group2",
1023                "uri": "/2/groups/group2",
1024                },
1025               ]
1026     self.rapi.AddResponse(serializer.DumpJson(groups))
1027     self.assertEqual(["group1", "group2"], self.client.GetGroups())
1028     self.assertHandler(rlib2.R_2_groups)
1029
1030   def testGetGroupsBulk(self):
1031     groups = [{"name": "group1",
1032                "uri": "/2/groups/group1",
1033                "node_cnt": 2,
1034                "node_list": ["gnt1.test",
1035                              "gnt2.test",
1036                              ],
1037                },
1038               {"name": "group2",
1039                "uri": "/2/groups/group2",
1040                "node_cnt": 1,
1041                "node_list": ["gnt3.test",
1042                              ],
1043                },
1044               ]
1045     self.rapi.AddResponse(serializer.DumpJson(groups))
1046
1047     self.assertEqual(groups, self.client.GetGroups(bulk=True))
1048     self.assertHandler(rlib2.R_2_groups)
1049     self.assertBulk()
1050
1051   def testGetGroup(self):
1052     group = {"ctime": None,
1053              "name": "default",
1054              }
1055     self.rapi.AddResponse(serializer.DumpJson(group))
1056     self.assertEqual({"ctime": None, "name": "default"},
1057                      self.client.GetGroup("default"))
1058     self.assertHandler(rlib2.R_2_groups_name)
1059     self.assertItems(["default"])
1060
1061   def testCreateGroup(self):
1062     self.rapi.AddResponse("12345")
1063     job_id = self.client.CreateGroup("newgroup", dry_run=True)
1064     self.assertEqual(job_id, 12345)
1065     self.assertHandler(rlib2.R_2_groups)
1066     self.assertDryRun()
1067
1068   def testDeleteGroup(self):
1069     self.rapi.AddResponse("12346")
1070     job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1071     self.assertEqual(job_id, 12346)
1072     self.assertHandler(rlib2.R_2_groups_name)
1073     self.assertDryRun()
1074
1075   def testRenameGroup(self):
1076     self.rapi.AddResponse("12347")
1077     job_id = self.client.RenameGroup("oldname", "newname")
1078     self.assertEqual(job_id, 12347)
1079     self.assertHandler(rlib2.R_2_groups_name_rename)
1080
1081   def testModifyGroup(self):
1082     self.rapi.AddResponse("12348")
1083     job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1084     self.assertEqual(job_id, 12348)
1085     self.assertHandler(rlib2.R_2_groups_name_modify)
1086
1087   def testAssignGroupNodes(self):
1088     self.rapi.AddResponse("12349")
1089     job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1090                                           force=True, dry_run=True)
1091     self.assertEqual(job_id, 12349)
1092     self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1093     self.assertDryRun()
1094     self.assertUseForce()
1095
1096   def testModifyInstance(self):
1097     self.rapi.AddResponse("23681")
1098     job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1099     self.assertEqual(job_id, 23681)
1100     self.assertItems(["inst7210"])
1101     self.assertHandler(rlib2.R_2_instances_name_modify)
1102     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1103                      { "os_name": "linux", })
1104
1105   def testModifyCluster(self):
1106     for mnh in [None, False, True]:
1107       self.rapi.AddResponse("14470")
1108       self.assertEqual(14470,
1109         self.client.ModifyCluster(maintain_node_health=mnh))
1110       self.assertHandler(rlib2.R_2_cluster_modify)
1111       self.assertItems([])
1112       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1113       self.assertEqual(len(data), 1)
1114       self.assertEqual(data["maintain_node_health"], mnh)
1115       self.assertEqual(self.rapi.CountPending(), 0)
1116
1117   def testRedistributeConfig(self):
1118     self.rapi.AddResponse("3364")
1119     job_id = self.client.RedistributeConfig()
1120     self.assertEqual(job_id, 3364)
1121     self.assertItems([])
1122     self.assertHandler(rlib2.R_2_redist_config)
1123
1124   def testActivateInstanceDisks(self):
1125     self.rapi.AddResponse("23547")
1126     job_id = self.client.ActivateInstanceDisks("inst28204")
1127     self.assertEqual(job_id, 23547)
1128     self.assertItems(["inst28204"])
1129     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1130     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1131
1132   def testActivateInstanceDisksIgnoreSize(self):
1133     self.rapi.AddResponse("11044")
1134     job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1135     self.assertEqual(job_id, 11044)
1136     self.assertItems(["inst28204"])
1137     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1138     self.assertQuery("ignore_size", ["1"])
1139
1140   def testDeactivateInstanceDisks(self):
1141     self.rapi.AddResponse("14591")
1142     job_id = self.client.DeactivateInstanceDisks("inst28234")
1143     self.assertEqual(job_id, 14591)
1144     self.assertItems(["inst28234"])
1145     self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1146     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1147
1148   def testRecreateInstanceDisks(self):
1149     self.rapi.AddResponse("13553")
1150     job_id = self.client.RecreateInstanceDisks("inst23153")
1151     self.assertEqual(job_id, 13553)
1152     self.assertItems(["inst23153"])
1153     self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1154     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1155
1156   def testGetInstanceConsole(self):
1157     self.rapi.AddResponse("26876")
1158     job_id = self.client.GetInstanceConsole("inst21491")
1159     self.assertEqual(job_id, 26876)
1160     self.assertItems(["inst21491"])
1161     self.assertHandler(rlib2.R_2_instances_name_console)
1162     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1163     self.assertFalse(self.rapi.GetLastRequestData())
1164
1165   def testGrowInstanceDisk(self):
1166     for idx, wait_for_sync in enumerate([None, False, True]):
1167       amount = 128 + (512 * idx)
1168       self.assertEqual(self.rapi.CountPending(), 0)
1169       self.rapi.AddResponse("30783")
1170       self.assertEqual(30783,
1171         self.client.GrowInstanceDisk("eze8ch", idx, amount,
1172                                      wait_for_sync=wait_for_sync))
1173       self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1174       self.assertItems(["eze8ch", str(idx)])
1175       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1176       if wait_for_sync is None:
1177         self.assertEqual(len(data), 1)
1178         self.assert_("wait_for_sync" not in data)
1179       else:
1180         self.assertEqual(len(data), 2)
1181         self.assertEqual(data["wait_for_sync"], wait_for_sync)
1182       self.assertEqual(data["amount"], amount)
1183       self.assertEqual(self.rapi.CountPending(), 0)
1184
1185   def testGetGroupTags(self):
1186     self.rapi.AddResponse("[]")
1187     self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1188     self.assertHandler(rlib2.R_2_groups_name_tags)
1189     self.assertItems(["fooGroup"])
1190
1191   def testAddGroupTags(self):
1192     self.rapi.AddResponse("1234")
1193     self.assertEqual(1234,
1194         self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1195     self.assertHandler(rlib2.R_2_groups_name_tags)
1196     self.assertItems(["fooGroup"])
1197     self.assertDryRun()
1198     self.assertQuery("tag", ["awesome"])
1199
1200   def testDeleteGroupTags(self):
1201     self.rapi.AddResponse("25826")
1202     self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1203                                                         dry_run=True))
1204     self.assertHandler(rlib2.R_2_groups_name_tags)
1205     self.assertItems(["foo"])
1206     self.assertDryRun()
1207     self.assertQuery("tag", ["awesome"])
1208
1209   def testQuery(self):
1210     for idx, what in enumerate(constants.QR_VIA_RAPI):
1211       for idx2, qfilter in enumerate([None, ["?", "name"]]):
1212         job_id = 11010 + (idx << 4) + (idx2 << 16)
1213         fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1214
1215         self.rapi.AddResponse(str(job_id))
1216         self.assertEqual(self.client.Query(what, fields, qfilter=qfilter),
1217                          job_id)
1218         self.assertItems([what])
1219         self.assertHandler(rlib2.R_2_query)
1220         self.assertFalse(self.rapi.GetLastHandler().queryargs)
1221         data = serializer.LoadJson(self.rapi.GetLastRequestData())
1222         self.assertEqual(data["fields"], fields)
1223         if qfilter is None:
1224           self.assertTrue("qfilter" not in data)
1225         else:
1226           self.assertEqual(data["qfilter"], qfilter)
1227         self.assertEqual(self.rapi.CountPending(), 0)
1228
1229   def testQueryFields(self):
1230     exp_result = objects.QueryFieldsResponse(fields=[
1231       objects.QueryFieldDefinition(name="pnode", title="PNode",
1232                                    kind=constants.QFT_NUMBER),
1233       objects.QueryFieldDefinition(name="other", title="Other",
1234                                    kind=constants.QFT_BOOL),
1235       ])
1236
1237     for what in constants.QR_VIA_RAPI:
1238       for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1239         self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1240         result = self.client.QueryFields(what, fields=fields)
1241         self.assertItems([what])
1242         self.assertHandler(rlib2.R_2_query_fields)
1243         self.assertFalse(self.rapi.GetLastRequestData())
1244
1245         queryargs = self.rapi.GetLastHandler().queryargs
1246         if fields is None:
1247           self.assertFalse(queryargs)
1248         else:
1249           self.assertEqual(queryargs, {
1250             "fields": [",".join(fields)],
1251             })
1252
1253         self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1254                          exp_result.ToDict())
1255
1256         self.assertEqual(self.rapi.CountPending(), 0)
1257
1258   def testWaitForJobCompletionNoChange(self):
1259     resp = serializer.DumpJson({
1260       "status": constants.JOB_STATUS_WAITING,
1261       })
1262
1263     for retries in [1, 5, 25]:
1264       for _ in range(retries):
1265         self.rapi.AddResponse(resp)
1266
1267       self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1268                                                         retries=retries))
1269       self.assertHandler(rlib2.R_2_jobs_id)
1270       self.assertItems(["22789"])
1271
1272       self.assertEqual(self.rapi.CountPending(), 0)
1273
1274   def testWaitForJobCompletionAlreadyFinished(self):
1275     self.rapi.AddResponse(serializer.DumpJson({
1276       "status": constants.JOB_STATUS_SUCCESS,
1277       }))
1278
1279     self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1280                                                      retries=1))
1281     self.assertHandler(rlib2.R_2_jobs_id)
1282     self.assertItems(["22793"])
1283
1284     self.assertEqual(self.rapi.CountPending(), 0)
1285
1286   def testWaitForJobCompletionEmptyResponse(self):
1287     self.rapi.AddResponse("{}")
1288     self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1289                                                      retries=10))
1290     self.assertHandler(rlib2.R_2_jobs_id)
1291     self.assertItems(["22793"])
1292
1293     self.assertEqual(self.rapi.CountPending(), 0)
1294
1295   def testWaitForJobCompletionOutOfRetries(self):
1296     for retries in [3, 10, 21]:
1297       for _ in range(retries):
1298         self.rapi.AddResponse(serializer.DumpJson({
1299           "status": constants.JOB_STATUS_RUNNING,
1300           }))
1301
1302       self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1303                                                         retries=retries - 1))
1304       self.assertHandler(rlib2.R_2_jobs_id)
1305       self.assertItems(["30948"])
1306
1307       self.assertEqual(self.rapi.CountPending(), 1)
1308       self.rapi.ResetResponses()
1309
1310   def testWaitForJobCompletionSuccessAndFailure(self):
1311     for retries in [1, 4, 13]:
1312       for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1313                                     (True, constants.JOB_STATUS_SUCCESS)]:
1314         for _ in range(retries):
1315           self.rapi.AddResponse(serializer.DumpJson({
1316             "status": constants.JOB_STATUS_RUNNING,
1317             }))
1318
1319         self.rapi.AddResponse(serializer.DumpJson({
1320           "status": end_status,
1321           }))
1322
1323         result = self.client.WaitForJobCompletion(3187, period=None,
1324                                                   retries=retries + 1)
1325         self.assertEqual(result, success)
1326         self.assertHandler(rlib2.R_2_jobs_id)
1327         self.assertItems(["3187"])
1328
1329         self.assertEqual(self.rapi.CountPending(), 0)
1330
1331
1332 class RapiTestRunner(unittest.TextTestRunner):
1333   def run(self, *args):
1334     global _used_handlers
1335     assert _used_handlers is None
1336
1337     _used_handlers = set()
1338     try:
1339       # Run actual tests
1340       result = unittest.TextTestRunner.run(self, *args)
1341
1342       diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1343              _KNOWN_UNUSED)
1344       if diff:
1345         raise AssertionError("The following RAPI resources were not used by the"
1346                              " RAPI client: %r" % utils.CommaJoin(diff))
1347     finally:
1348       # Reset global variable
1349       _used_handlers = None
1350
1351     return result
1352
1353
1354 if __name__ == '__main__':
1355   client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)