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