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