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