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