555ee21be4ca464a0149bdb80eb6a09e4f275405
[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   def testGetJobStatus(self):
841     self.rapi.AddResponse("{\"foo\": \"bar\"}")
842     self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
843     self.assertHandler(rlib2.R_2_jobs_id)
844     self.assertItems(["1234"])
845
846   def testWaitForJobChange(self):
847     fields = ["id", "summary"]
848     expected = {
849       "job_info": [123, "something"],
850       "log_entries": [],
851       }
852
853     self.rapi.AddResponse(serializer.DumpJson(expected))
854     result = self.client.WaitForJobChange(123, fields, [], -1)
855     self.assertEqualValues(expected, result)
856     self.assertHandler(rlib2.R_2_jobs_id_wait)
857     self.assertItems(["123"])
858
859   def testCancelJob(self):
860     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
861     self.assertEqual([True, "Job 123 will be canceled"],
862                      self.client.CancelJob(999, dry_run=True))
863     self.assertHandler(rlib2.R_2_jobs_id)
864     self.assertItems(["999"])
865     self.assertDryRun()
866
867   def testGetNodes(self):
868     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
869                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
870     self.assertEqual(["node1", "node2"], self.client.GetNodes())
871     self.assertHandler(rlib2.R_2_nodes)
872
873     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
874                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
875     self.assertEqual([{"id": "node1", "uri": "uri1"},
876                       {"id": "node2", "uri": "uri2"}],
877                      self.client.GetNodes(bulk=True))
878     self.assertHandler(rlib2.R_2_nodes)
879     self.assertBulk()
880
881   def testGetNode(self):
882     self.rapi.AddResponse("{}")
883     self.assertEqual({}, self.client.GetNode("node-foo"))
884     self.assertHandler(rlib2.R_2_nodes_name)
885     self.assertItems(["node-foo"])
886
887   def testEvacuateNode(self):
888     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
889     self.rapi.AddResponse("9876")
890     job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
891     self.assertEqual(9876, job_id)
892     self.assertHandler(rlib2.R_2_nodes_name_evacuate)
893     self.assertItems(["node-1"])
894     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
895                      { "remote_node": "node-2", })
896     self.assertEqual(self.rapi.CountPending(), 0)
897
898     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_EVAC_RES1]))
899     self.rapi.AddResponse("8888")
900     job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True,
901                                       mode=constants.NODE_EVAC_ALL,
902                                       early_release=True)
903     self.assertEqual(8888, job_id)
904     self.assertItems(["node-3"])
905     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()), {
906       "iallocator": "hail",
907       "mode": "all",
908       "early_release": True,
909       })
910     self.assertDryRun()
911
912     self.assertRaises(client.GanetiApiError,
913                       self.client.EvacuateNode,
914                       "node-4", iallocator="hail", remote_node="node-5")
915     self.assertEqual(self.rapi.CountPending(), 0)
916
917   def testEvacuateNodeOldResponse(self):
918     self.rapi.AddResponse(serializer.DumpJson([]))
919     self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
920                       "node-4", accept_old=False)
921     self.assertEqual(self.rapi.CountPending(), 0)
922
923     for mode in [client.NODE_EVAC_PRI, client.NODE_EVAC_ALL]:
924       self.rapi.AddResponse(serializer.DumpJson([]))
925       self.assertRaises(client.GanetiApiError, self.client.EvacuateNode,
926                         "node-4", accept_old=True, mode=mode)
927       self.assertEqual(self.rapi.CountPending(), 0)
928
929     self.rapi.AddResponse(serializer.DumpJson([]))
930     self.rapi.AddResponse(serializer.DumpJson("21533"))
931     result = self.client.EvacuateNode("node-3", iallocator="hail",
932                                       dry_run=True, accept_old=True,
933                                       mode=client.NODE_EVAC_SEC,
934                                       early_release=True)
935     self.assertEqual(result, "21533")
936     self.assertItems(["node-3"])
937     self.assertQuery("iallocator", ["hail"])
938     self.assertQuery("early_release", ["1"])
939     self.assertFalse(self.rapi.GetLastRequestData())
940     self.assertDryRun()
941     self.assertEqual(self.rapi.CountPending(), 0)
942
943   def testMigrateNode(self):
944     self.rapi.AddResponse(serializer.DumpJson([]))
945     self.rapi.AddResponse("1111")
946     self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
947     self.assertHandler(rlib2.R_2_nodes_name_migrate)
948     self.assertItems(["node-a"])
949     self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
950     self.assertDryRun()
951     self.assertFalse(self.rapi.GetLastRequestData())
952
953     self.rapi.AddResponse(serializer.DumpJson([]))
954     self.rapi.AddResponse("1112")
955     self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
956                                                    mode="live"))
957     self.assertHandler(rlib2.R_2_nodes_name_migrate)
958     self.assertItems(["node-a"])
959     self.assertQuery("mode", ["live"])
960     self.assertDryRun()
961     self.assertFalse(self.rapi.GetLastRequestData())
962
963     self.rapi.AddResponse(serializer.DumpJson([]))
964     self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
965                       "node-c", target_node="foonode")
966     self.assertEqual(self.rapi.CountPending(), 0)
967
968   def testMigrateNodeBodyData(self):
969     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
970     self.rapi.AddResponse("27539")
971     self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
972                                                     mode="live"))
973     self.assertHandler(rlib2.R_2_nodes_name_migrate)
974     self.assertItems(["node-a"])
975     self.assertFalse(self.rapi.GetLastHandler().queryargs)
976     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
977                      { "mode": "live", })
978
979     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
980     self.rapi.AddResponse("14219")
981     self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
982                                                     target_node="node9",
983                                                     iallocator="ial"))
984     self.assertHandler(rlib2.R_2_nodes_name_migrate)
985     self.assertItems(["node-x"])
986     self.assertDryRun()
987     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
988                      { "target_node": "node9", "iallocator": "ial", })
989
990     self.assertEqual(self.rapi.CountPending(), 0)
991
992   def testGetNodeRole(self):
993     self.rapi.AddResponse("\"master\"")
994     self.assertEqual("master", self.client.GetNodeRole("node-a"))
995     self.assertHandler(rlib2.R_2_nodes_name_role)
996     self.assertItems(["node-a"])
997
998   def testSetNodeRole(self):
999     self.rapi.AddResponse("789")
1000     self.assertEqual(789,
1001         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
1002     self.assertHandler(rlib2.R_2_nodes_name_role)
1003     self.assertItems(["node-foo"])
1004     self.assertQuery("force", ["1"])
1005     self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
1006
1007   def testPowercycleNode(self):
1008     self.rapi.AddResponse("23051")
1009     self.assertEqual(23051,
1010         self.client.PowercycleNode("node5468", force=True))
1011     self.assertHandler(rlib2.R_2_nodes_name_powercycle)
1012     self.assertItems(["node5468"])
1013     self.assertQuery("force", ["1"])
1014     self.assertFalse(self.rapi.GetLastRequestData())
1015     self.assertEqual(self.rapi.CountPending(), 0)
1016
1017   def testModifyNode(self):
1018     self.rapi.AddResponse("3783")
1019     job_id = self.client.ModifyNode("node16979.example.com", drained=True)
1020     self.assertEqual(job_id, 3783)
1021     self.assertHandler(rlib2.R_2_nodes_name_modify)
1022     self.assertItems(["node16979.example.com"])
1023     self.assertEqual(self.rapi.CountPending(), 0)
1024
1025   def testGetNodeStorageUnits(self):
1026     self.rapi.AddResponse("42")
1027     self.assertEqual(42,
1028         self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
1029     self.assertHandler(rlib2.R_2_nodes_name_storage)
1030     self.assertItems(["node-x"])
1031     self.assertQuery("storage_type", ["lvm-pv"])
1032     self.assertQuery("output_fields", ["fields"])
1033
1034   def testModifyNodeStorageUnits(self):
1035     self.rapi.AddResponse("14")
1036     self.assertEqual(14,
1037         self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
1038     self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1039     self.assertItems(["node-z"])
1040     self.assertQuery("storage_type", ["lvm-pv"])
1041     self.assertQuery("name", ["hda"])
1042     self.assertQuery("allocatable", None)
1043
1044     for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
1045       self.rapi.AddResponse("7205")
1046       job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
1047                                                   allocatable=allocatable)
1048       self.assertEqual(7205, job_id)
1049       self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
1050       self.assertItems(["node-z"])
1051       self.assertQuery("storage_type", ["lvm-pv"])
1052       self.assertQuery("name", ["hda"])
1053       self.assertQuery("allocatable", [query_allocatable])
1054
1055   def testRepairNodeStorageUnits(self):
1056     self.rapi.AddResponse("99")
1057     self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
1058                                                             "hda"))
1059     self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
1060     self.assertItems(["node-z"])
1061     self.assertQuery("storage_type", ["lvm-pv"])
1062     self.assertQuery("name", ["hda"])
1063
1064   def testGetNodeTags(self):
1065     self.rapi.AddResponse("[\"fry\", \"bender\"]")
1066     self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
1067     self.assertHandler(rlib2.R_2_nodes_name_tags)
1068     self.assertItems(["node-k"])
1069
1070   def testAddNodeTags(self):
1071     self.rapi.AddResponse("1234")
1072     self.assertEqual(1234,
1073         self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
1074     self.assertHandler(rlib2.R_2_nodes_name_tags)
1075     self.assertItems(["node-v"])
1076     self.assertDryRun()
1077     self.assertQuery("tag", ["awesome"])
1078
1079   def testDeleteNodeTags(self):
1080     self.rapi.AddResponse("16861")
1081     self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
1082                                                        dry_run=True))
1083     self.assertHandler(rlib2.R_2_nodes_name_tags)
1084     self.assertItems(["node-w"])
1085     self.assertDryRun()
1086     self.assertQuery("tag", ["awesome"])
1087
1088   def testGetGroups(self):
1089     groups = [{"name": "group1",
1090                "uri": "/2/groups/group1",
1091                },
1092               {"name": "group2",
1093                "uri": "/2/groups/group2",
1094                },
1095               ]
1096     self.rapi.AddResponse(serializer.DumpJson(groups))
1097     self.assertEqual(["group1", "group2"], self.client.GetGroups())
1098     self.assertHandler(rlib2.R_2_groups)
1099
1100   def testGetGroupsBulk(self):
1101     groups = [{"name": "group1",
1102                "uri": "/2/groups/group1",
1103                "node_cnt": 2,
1104                "node_list": ["gnt1.test",
1105                              "gnt2.test",
1106                              ],
1107                },
1108               {"name": "group2",
1109                "uri": "/2/groups/group2",
1110                "node_cnt": 1,
1111                "node_list": ["gnt3.test",
1112                              ],
1113                },
1114               ]
1115     self.rapi.AddResponse(serializer.DumpJson(groups))
1116
1117     self.assertEqual(groups, self.client.GetGroups(bulk=True))
1118     self.assertHandler(rlib2.R_2_groups)
1119     self.assertBulk()
1120
1121   def testGetGroup(self):
1122     group = {"ctime": None,
1123              "name": "default",
1124              }
1125     self.rapi.AddResponse(serializer.DumpJson(group))
1126     self.assertEqual({"ctime": None, "name": "default"},
1127                      self.client.GetGroup("default"))
1128     self.assertHandler(rlib2.R_2_groups_name)
1129     self.assertItems(["default"])
1130
1131   def testCreateGroup(self):
1132     self.rapi.AddResponse("12345")
1133     job_id = self.client.CreateGroup("newgroup", dry_run=True)
1134     self.assertEqual(job_id, 12345)
1135     self.assertHandler(rlib2.R_2_groups)
1136     self.assertDryRun()
1137
1138   def testDeleteGroup(self):
1139     self.rapi.AddResponse("12346")
1140     job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1141     self.assertEqual(job_id, 12346)
1142     self.assertHandler(rlib2.R_2_groups_name)
1143     self.assertDryRun()
1144
1145   def testRenameGroup(self):
1146     self.rapi.AddResponse("12347")
1147     job_id = self.client.RenameGroup("oldname", "newname")
1148     self.assertEqual(job_id, 12347)
1149     self.assertHandler(rlib2.R_2_groups_name_rename)
1150
1151   def testModifyGroup(self):
1152     self.rapi.AddResponse("12348")
1153     job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1154     self.assertEqual(job_id, 12348)
1155     self.assertHandler(rlib2.R_2_groups_name_modify)
1156
1157   def testAssignGroupNodes(self):
1158     self.rapi.AddResponse("12349")
1159     job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1160                                           force=True, dry_run=True)
1161     self.assertEqual(job_id, 12349)
1162     self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1163     self.assertDryRun()
1164     self.assertUseForce()
1165
1166   def testGetNetworksBulk(self):
1167     networks = [{"name": "network1",
1168                "uri": "/2/networks/network1",
1169                "network": "192.168.0.0/24",
1170                },
1171               {"name": "network2",
1172                "uri": "/2/networks/network2",
1173                "network": "192.168.0.0/24",
1174                },
1175               ]
1176     self.rapi.AddResponse(serializer.DumpJson(networks))
1177
1178     self.assertEqual(networks, self.client.GetNetworks(bulk=True))
1179     self.assertHandler(rlib2.R_2_networks)
1180     self.assertBulk()
1181
1182   def testGetNetwork(self):
1183     network = {"ctime": None,
1184                "name": "network1",
1185                }
1186     self.rapi.AddResponse(serializer.DumpJson(network))
1187     self.assertEqual({"ctime": None, "name": "network1"},
1188                      self.client.GetNetwork("network1"))
1189     self.assertHandler(rlib2.R_2_networks_name)
1190     self.assertItems(["network1"])
1191
1192   def testCreateNetwork(self):
1193     self.rapi.AddResponse("12345")
1194     job_id = self.client.CreateNetwork("newnetwork", network="192.168.0.0/24",
1195                                        dry_run=True)
1196     self.assertEqual(job_id, 12345)
1197     self.assertHandler(rlib2.R_2_networks)
1198     self.assertDryRun()
1199
1200   def testModifyNetwork(self):
1201     self.rapi.AddResponse("12346")
1202     job_id = self.client.ModifyNetwork("mynetwork", gateway="192.168.0.10",
1203                                      dry_run=True)
1204     self.assertEqual(job_id, 12346)
1205     self.assertHandler(rlib2.R_2_networks_name_modify)
1206
1207   def testDeleteNetwork(self):
1208     self.rapi.AddResponse("12347")
1209     job_id = self.client.DeleteNetwork("newnetwork", dry_run=True)
1210     self.assertEqual(job_id, 12347)
1211     self.assertHandler(rlib2.R_2_networks_name)
1212     self.assertDryRun()
1213
1214   def testConnectNetwork(self):
1215     self.rapi.AddResponse("12348")
1216     job_id = self.client.ConnectNetwork("mynetwork", "default",
1217                                         "bridged", "br0", dry_run=True)
1218     self.assertEqual(job_id, 12348)
1219     self.assertHandler(rlib2.R_2_networks_name_connect)
1220     self.assertDryRun()
1221
1222   def testDisconnectNetwork(self):
1223     self.rapi.AddResponse("12349")
1224     job_id = self.client.DisconnectNetwork("mynetwork", "default", dry_run=True)
1225     self.assertEqual(job_id, 12349)
1226     self.assertHandler(rlib2.R_2_networks_name_disconnect)
1227     self.assertDryRun()
1228
1229   def testGetNetworkTags(self):
1230     self.rapi.AddResponse("[]")
1231     self.assertEqual([], self.client.GetNetworkTags("fooNetwork"))
1232     self.assertHandler(rlib2.R_2_networks_name_tags)
1233     self.assertItems(["fooNetwork"])
1234
1235   def testAddNetworkTags(self):
1236     self.rapi.AddResponse("1234")
1237     self.assertEqual(1234,
1238         self.client.AddNetworkTags("fooNetwork", ["awesome"], dry_run=True))
1239     self.assertHandler(rlib2.R_2_networks_name_tags)
1240     self.assertItems(["fooNetwork"])
1241     self.assertDryRun()
1242     self.assertQuery("tag", ["awesome"])
1243
1244   def testDeleteNetworkTags(self):
1245     self.rapi.AddResponse("25826")
1246     self.assertEqual(25826, self.client.DeleteNetworkTags("foo", ["awesome"],
1247                                                           dry_run=True))
1248     self.assertHandler(rlib2.R_2_networks_name_tags)
1249     self.assertItems(["foo"])
1250     self.assertDryRun()
1251     self.assertQuery("tag", ["awesome"])
1252
1253   def testModifyInstance(self):
1254     self.rapi.AddResponse("23681")
1255     job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1256     self.assertEqual(job_id, 23681)
1257     self.assertItems(["inst7210"])
1258     self.assertHandler(rlib2.R_2_instances_name_modify)
1259     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1260                      { "os_name": "linux", })
1261
1262   def testModifyCluster(self):
1263     for mnh in [None, False, True]:
1264       self.rapi.AddResponse("14470")
1265       self.assertEqual(14470,
1266         self.client.ModifyCluster(maintain_node_health=mnh))
1267       self.assertHandler(rlib2.R_2_cluster_modify)
1268       self.assertItems([])
1269       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1270       self.assertEqual(len(data), 1)
1271       self.assertEqual(data["maintain_node_health"], mnh)
1272       self.assertEqual(self.rapi.CountPending(), 0)
1273
1274   def testRedistributeConfig(self):
1275     self.rapi.AddResponse("3364")
1276     job_id = self.client.RedistributeConfig()
1277     self.assertEqual(job_id, 3364)
1278     self.assertItems([])
1279     self.assertHandler(rlib2.R_2_redist_config)
1280
1281   def testActivateInstanceDisks(self):
1282     self.rapi.AddResponse("23547")
1283     job_id = self.client.ActivateInstanceDisks("inst28204")
1284     self.assertEqual(job_id, 23547)
1285     self.assertItems(["inst28204"])
1286     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1287     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1288
1289   def testActivateInstanceDisksIgnoreSize(self):
1290     self.rapi.AddResponse("11044")
1291     job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1292     self.assertEqual(job_id, 11044)
1293     self.assertItems(["inst28204"])
1294     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1295     self.assertQuery("ignore_size", ["1"])
1296
1297   def testDeactivateInstanceDisks(self):
1298     self.rapi.AddResponse("14591")
1299     job_id = self.client.DeactivateInstanceDisks("inst28234")
1300     self.assertEqual(job_id, 14591)
1301     self.assertItems(["inst28234"])
1302     self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1303     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1304
1305   def testRecreateInstanceDisks(self):
1306     self.rapi.AddResponse("13553")
1307     job_id = self.client.RecreateInstanceDisks("inst23153")
1308     self.assertEqual(job_id, 13553)
1309     self.assertItems(["inst23153"])
1310     self.assertHandler(rlib2.R_2_instances_name_recreate_disks)
1311     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1312
1313   def testGetInstanceConsole(self):
1314     self.rapi.AddResponse("26876")
1315     job_id = self.client.GetInstanceConsole("inst21491")
1316     self.assertEqual(job_id, 26876)
1317     self.assertItems(["inst21491"])
1318     self.assertHandler(rlib2.R_2_instances_name_console)
1319     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1320     self.assertFalse(self.rapi.GetLastRequestData())
1321
1322   def testGrowInstanceDisk(self):
1323     for idx, wait_for_sync in enumerate([None, False, True]):
1324       amount = 128 + (512 * idx)
1325       self.assertEqual(self.rapi.CountPending(), 0)
1326       self.rapi.AddResponse("30783")
1327       self.assertEqual(30783,
1328         self.client.GrowInstanceDisk("eze8ch", idx, amount,
1329                                      wait_for_sync=wait_for_sync))
1330       self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1331       self.assertItems(["eze8ch", str(idx)])
1332       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1333       if wait_for_sync is None:
1334         self.assertEqual(len(data), 1)
1335         self.assert_("wait_for_sync" not in data)
1336       else:
1337         self.assertEqual(len(data), 2)
1338         self.assertEqual(data["wait_for_sync"], wait_for_sync)
1339       self.assertEqual(data["amount"], amount)
1340       self.assertEqual(self.rapi.CountPending(), 0)
1341
1342   def testGetGroupTags(self):
1343     self.rapi.AddResponse("[]")
1344     self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1345     self.assertHandler(rlib2.R_2_groups_name_tags)
1346     self.assertItems(["fooGroup"])
1347
1348   def testAddGroupTags(self):
1349     self.rapi.AddResponse("1234")
1350     self.assertEqual(1234,
1351         self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1352     self.assertHandler(rlib2.R_2_groups_name_tags)
1353     self.assertItems(["fooGroup"])
1354     self.assertDryRun()
1355     self.assertQuery("tag", ["awesome"])
1356
1357   def testDeleteGroupTags(self):
1358     self.rapi.AddResponse("25826")
1359     self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1360                                                         dry_run=True))
1361     self.assertHandler(rlib2.R_2_groups_name_tags)
1362     self.assertItems(["foo"])
1363     self.assertDryRun()
1364     self.assertQuery("tag", ["awesome"])
1365
1366   def testQuery(self):
1367     for idx, what in enumerate(constants.QR_VIA_RAPI):
1368       for idx2, qfilter in enumerate([None, ["?", "name"]]):
1369         job_id = 11010 + (idx << 4) + (idx2 << 16)
1370         fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1371
1372         self.rapi.AddResponse(str(job_id))
1373         self.assertEqual(self.client.Query(what, fields, qfilter=qfilter),
1374                          job_id)
1375         self.assertItems([what])
1376         self.assertHandler(rlib2.R_2_query)
1377         self.assertFalse(self.rapi.GetLastHandler().queryargs)
1378         data = serializer.LoadJson(self.rapi.GetLastRequestData())
1379         self.assertEqual(data["fields"], fields)
1380         if qfilter is None:
1381           self.assertTrue("qfilter" not in data)
1382         else:
1383           self.assertEqual(data["qfilter"], qfilter)
1384         self.assertEqual(self.rapi.CountPending(), 0)
1385
1386   def testQueryFields(self):
1387     exp_result = objects.QueryFieldsResponse(fields=[
1388       objects.QueryFieldDefinition(name="pnode", title="PNode",
1389                                    kind=constants.QFT_NUMBER),
1390       objects.QueryFieldDefinition(name="other", title="Other",
1391                                    kind=constants.QFT_BOOL),
1392       ])
1393
1394     for what in constants.QR_VIA_RAPI:
1395       for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1396         self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1397         result = self.client.QueryFields(what, fields=fields)
1398         self.assertItems([what])
1399         self.assertHandler(rlib2.R_2_query_fields)
1400         self.assertFalse(self.rapi.GetLastRequestData())
1401
1402         queryargs = self.rapi.GetLastHandler().queryargs
1403         if fields is None:
1404           self.assertFalse(queryargs)
1405         else:
1406           self.assertEqual(queryargs, {
1407             "fields": [",".join(fields)],
1408             })
1409
1410         self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1411                          exp_result.ToDict())
1412
1413         self.assertEqual(self.rapi.CountPending(), 0)
1414
1415   def testWaitForJobCompletionNoChange(self):
1416     resp = serializer.DumpJson({
1417       "status": constants.JOB_STATUS_WAITING,
1418       })
1419
1420     for retries in [1, 5, 25]:
1421       for _ in range(retries):
1422         self.rapi.AddResponse(resp)
1423
1424       self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1425                                                         retries=retries))
1426       self.assertHandler(rlib2.R_2_jobs_id)
1427       self.assertItems(["22789"])
1428
1429       self.assertEqual(self.rapi.CountPending(), 0)
1430
1431   def testWaitForJobCompletionAlreadyFinished(self):
1432     self.rapi.AddResponse(serializer.DumpJson({
1433       "status": constants.JOB_STATUS_SUCCESS,
1434       }))
1435
1436     self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1437                                                      retries=1))
1438     self.assertHandler(rlib2.R_2_jobs_id)
1439     self.assertItems(["22793"])
1440
1441     self.assertEqual(self.rapi.CountPending(), 0)
1442
1443   def testWaitForJobCompletionEmptyResponse(self):
1444     self.rapi.AddResponse("{}")
1445     self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1446                                                      retries=10))
1447     self.assertHandler(rlib2.R_2_jobs_id)
1448     self.assertItems(["22793"])
1449
1450     self.assertEqual(self.rapi.CountPending(), 0)
1451
1452   def testWaitForJobCompletionOutOfRetries(self):
1453     for retries in [3, 10, 21]:
1454       for _ in range(retries):
1455         self.rapi.AddResponse(serializer.DumpJson({
1456           "status": constants.JOB_STATUS_RUNNING,
1457           }))
1458
1459       self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1460                                                         retries=retries - 1))
1461       self.assertHandler(rlib2.R_2_jobs_id)
1462       self.assertItems(["30948"])
1463
1464       self.assertEqual(self.rapi.CountPending(), 1)
1465       self.rapi.ResetResponses()
1466
1467   def testWaitForJobCompletionSuccessAndFailure(self):
1468     for retries in [1, 4, 13]:
1469       for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1470                                     (True, constants.JOB_STATUS_SUCCESS)]:
1471         for _ in range(retries):
1472           self.rapi.AddResponse(serializer.DumpJson({
1473             "status": constants.JOB_STATUS_RUNNING,
1474             }))
1475
1476         self.rapi.AddResponse(serializer.DumpJson({
1477           "status": end_status,
1478           }))
1479
1480         result = self.client.WaitForJobCompletion(3187, period=None,
1481                                                   retries=retries + 1)
1482         self.assertEqual(result, success)
1483         self.assertHandler(rlib2.R_2_jobs_id)
1484         self.assertItems(["3187"])
1485
1486         self.assertEqual(self.rapi.CountPending(), 0)
1487
1488
1489 class RapiTestRunner(unittest.TextTestRunner):
1490   def run(self, *args):
1491     global _used_handlers
1492     assert _used_handlers is None
1493
1494     _used_handlers = set()
1495     try:
1496       # Run actual tests
1497       result = unittest.TextTestRunner.run(self, *args)
1498
1499       diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1500              _KNOWN_UNUSED)
1501       if diff:
1502         raise AssertionError("The following RAPI resources were not used by the"
1503                              " RAPI client: %r" % utils.CommaJoin(diff))
1504     finally:
1505       # Reset global variable
1506       _used_handlers = None
1507
1508     return result
1509
1510
1511 if __name__ == "__main__":
1512   client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)