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