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