4 # Copyright (C) 2010 Google Inc.
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.
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.
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
22 """Script for unittesting the RAPI client module"""
30 from ganeti import constants
31 from ganeti import http
32 from ganeti import serializer
34 from ganeti.rapi import connector
35 from ganeti.rapi import rlib2
36 from ganeti.rapi import client
41 _URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
44 def _GetPathFromUri(uri):
45 """Gets the path and query from a URI.
48 match = _URI_RE.match(uri)
50 return match.groupdict()["path"]
56 def __init__(self, rapi):
61 def setopt(self, opt, value):
62 self._opts[opt] = value
64 def getopt(self, opt):
65 return self._opts.get(opt)
67 def unsetopt(self, opt):
68 self._opts.pop(opt, None)
70 def getinfo(self, info):
71 return self._info[info]
74 method = self._opts[pycurl.CUSTOMREQUEST]
75 url = self._opts[pycurl.URL]
76 request_body = self._opts[pycurl.POSTFIELDS]
77 writefn = self._opts[pycurl.WRITEFUNCTION]
79 path = _GetPathFromUri(url)
80 (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
82 self._info[pycurl.RESPONSE_CODE] = code
83 if resp_body is not None:
87 class RapiMock(object):
89 self._mapper = connector.Mapper()
91 self._last_handler = None
92 self._last_req_data = None
94 def AddResponse(self, response, code=200):
95 self._responses.insert(0, (code, response))
97 def CountPending(self):
98 return len(self._responses)
100 def GetLastHandler(self):
101 return self._last_handler
103 def GetLastRequestData(self):
104 return self._last_req_data
106 def FetchResponse(self, path, method, request_body):
107 self._last_req_data = request_body
110 HandlerClass, items, args = self._mapper.getController(path)
111 self._last_handler = HandlerClass(items, args, None)
112 if not hasattr(self._last_handler, method.upper()):
113 raise http.HttpNotImplemented(message="Method not implemented")
115 except http.HttpException, ex:
117 response = ex.message
119 if not self._responses:
120 raise Exception("No responses")
122 (code, response) = self._responses.pop()
124 return code, response
127 class TestConstants(unittest.TestCase):
129 self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
130 self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
131 self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
132 self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
133 self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
134 self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
135 self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
138 class RapiMockTest(unittest.TestCase):
142 self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
143 self.assertEqual((501, "Method not implemented"),
144 rapi.FetchResponse("/version", "POST", None))
145 rapi.AddResponse("2")
146 code, response = rapi.FetchResponse("/version", "GET", None)
147 self.assertEqual(200, code)
148 self.assertEqual("2", response)
149 self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
152 def _FakeNoSslPycurlVersion():
153 # Note: incomplete version tuple
154 return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
157 def _FakeFancySslPycurlVersion():
158 # Note: incomplete version tuple
159 return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
162 def _FakeOpenSslPycurlVersion():
163 # Note: incomplete version tuple
164 return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
167 def _FakeGnuTlsPycurlVersion():
168 # Note: incomplete version tuple
169 return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
172 class TestExtendedConfig(unittest.TestCase):
174 cl = client.GanetiRapiClient("master.example.com",
175 username="user", password="pw",
176 curl_factory=lambda: FakeCurl(RapiMock()))
178 curl = cl._CreateCurl()
179 self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
180 self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
182 def testInvalidAuth(self):
184 self.assertRaises(client.Error, client.GanetiRapiClient,
185 "master-a.example.com", password="pw")
187 self.assertRaises(client.Error, client.GanetiRapiClient,
188 "master-b.example.com", username="user")
190 def testCertVerifyInvalidCombinations(self):
191 self.assertRaises(client.Error, client.GenericCurlConfig,
192 use_curl_cabundle=True, cafile="cert1.pem")
193 self.assertRaises(client.Error, client.GenericCurlConfig,
194 use_curl_cabundle=True, capath="certs/")
195 self.assertRaises(client.Error, client.GenericCurlConfig,
196 use_curl_cabundle=True,
197 cafile="cert1.pem", capath="certs/")
199 def testProxySignalVerifyHostname(self):
200 for use_gnutls in [False, True]:
202 pcverfn = _FakeGnuTlsPycurlVersion
204 pcverfn = _FakeOpenSslPycurlVersion
206 for proxy in ["", "http://127.0.0.1:1234"]:
207 for use_signal in [False, True]:
208 for verify_hostname in [False, True]:
209 cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
210 verify_hostname=verify_hostname,
211 _pycurl_version_fn=pcverfn)
213 curl_factory = lambda: FakeCurl(RapiMock())
214 cl = client.GanetiRapiClient("master.example.com",
215 curl_config_fn=cfgfn,
216 curl_factory=curl_factory)
218 curl = cl._CreateCurl()
219 self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
220 self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
223 self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
225 self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
227 def testNoCertVerify(self):
228 cfgfn = client.GenericCurlConfig()
230 curl_factory = lambda: FakeCurl(RapiMock())
231 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
232 curl_factory=curl_factory)
234 curl = cl._CreateCurl()
235 self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
236 self.assertFalse(curl.getopt(pycurl.CAINFO))
237 self.assertFalse(curl.getopt(pycurl.CAPATH))
239 def testCertVerifyCurlBundle(self):
240 cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
242 curl_factory = lambda: FakeCurl(RapiMock())
243 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
244 curl_factory=curl_factory)
246 curl = cl._CreateCurl()
247 self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
248 self.assertFalse(curl.getopt(pycurl.CAINFO))
249 self.assertFalse(curl.getopt(pycurl.CAPATH))
251 def testCertVerifyCafile(self):
252 mycert = "/tmp/some/UNUSED/cert/file.pem"
253 cfgfn = client.GenericCurlConfig(cafile=mycert)
255 curl_factory = lambda: FakeCurl(RapiMock())
256 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
257 curl_factory=curl_factory)
259 curl = cl._CreateCurl()
260 self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
261 self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
262 self.assertFalse(curl.getopt(pycurl.CAPATH))
264 def testCertVerifyCapath(self):
265 certdir = "/tmp/some/UNUSED/cert/directory"
266 pcverfn = _FakeOpenSslPycurlVersion
267 cfgfn = client.GenericCurlConfig(capath=certdir,
268 _pycurl_version_fn=pcverfn)
270 curl_factory = lambda: FakeCurl(RapiMock())
271 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
272 curl_factory=curl_factory)
274 curl = cl._CreateCurl()
275 self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
276 self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
277 self.assertFalse(curl.getopt(pycurl.CAINFO))
279 def testCertVerifyCapathGnuTls(self):
280 certdir = "/tmp/some/UNUSED/cert/directory"
281 pcverfn = _FakeGnuTlsPycurlVersion
282 cfgfn = client.GenericCurlConfig(capath=certdir,
283 _pycurl_version_fn=pcverfn)
285 curl_factory = lambda: FakeCurl(RapiMock())
286 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
287 curl_factory=curl_factory)
289 self.assertRaises(client.Error, cl._CreateCurl)
291 def testCertVerifyNoSsl(self):
292 certdir = "/tmp/some/UNUSED/cert/directory"
293 pcverfn = _FakeNoSslPycurlVersion
294 cfgfn = client.GenericCurlConfig(capath=certdir,
295 _pycurl_version_fn=pcverfn)
297 curl_factory = lambda: FakeCurl(RapiMock())
298 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
299 curl_factory=curl_factory)
301 self.assertRaises(client.Error, cl._CreateCurl)
303 def testCertVerifyFancySsl(self):
304 certdir = "/tmp/some/UNUSED/cert/directory"
305 pcverfn = _FakeFancySslPycurlVersion
306 cfgfn = client.GenericCurlConfig(capath=certdir,
307 _pycurl_version_fn=pcverfn)
309 curl_factory = lambda: FakeCurl(RapiMock())
310 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
311 curl_factory=curl_factory)
313 self.assertRaises(NotImplementedError, cl._CreateCurl)
315 def testCertVerifyCapath(self):
316 for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
317 for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
318 cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
321 curl_factory = lambda: FakeCurl(RapiMock())
322 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
323 curl_factory=curl_factory)
325 curl = cl._CreateCurl()
326 self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
327 self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
330 class GanetiRapiClientTests(testutils.GanetiTestCase):
332 testutils.GanetiTestCase.setUp(self)
334 self.rapi = RapiMock()
335 self.curl = FakeCurl(self.rapi)
336 self.client = client.GanetiRapiClient("master.example.com",
337 curl_factory=lambda: self.curl)
339 def assertHandler(self, handler_cls):
340 self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
342 def assertQuery(self, key, value):
343 self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
345 def assertItems(self, items):
346 self.assertEqual(items, self.rapi.GetLastHandler().items)
348 def assertBulk(self):
349 self.assertTrue(self.rapi.GetLastHandler().useBulk())
351 def assertDryRun(self):
352 self.assertTrue(self.rapi.GetLastHandler().dryRun())
354 def testEncodeQuery(self):
371 self.assertEqualValues(self.client._EncodeQuery(query),
375 for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
376 self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
378 def testCurlSettings(self):
379 self.rapi.AddResponse("2")
380 self.assertEqual(2, self.client.GetVersion())
381 self.assertHandler(rlib2.R_version)
383 # Signals should be disabled by default
384 self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
386 # No auth and no proxy
387 self.assertFalse(self.curl.getopt(pycurl.USERPWD))
388 self.assert_(self.curl.getopt(pycurl.PROXY) is None)
390 # Content-type is required for requests
391 headers = self.curl.getopt(pycurl.HTTPHEADER)
392 self.assert_("Content-type: application/json" in headers)
394 def testHttpError(self):
395 self.rapi.AddResponse(None, code=404)
397 self.client.GetJobStatus(15140)
398 except client.GanetiApiError, err:
399 self.assertEqual(err.code, 404)
401 self.fail("Didn't raise exception")
403 def testGetVersion(self):
404 self.rapi.AddResponse("2")
405 self.assertEqual(2, self.client.GetVersion())
406 self.assertHandler(rlib2.R_version)
408 def testGetFeatures(self):
409 for features in [[], ["foo", "bar", "baz"]]:
410 self.rapi.AddResponse(serializer.DumpJson(features))
411 self.assertEqual(features, self.client.GetFeatures())
412 self.assertHandler(rlib2.R_2_features)
414 def testGetFeaturesNotFound(self):
415 self.rapi.AddResponse(None, code=404)
416 self.assertEqual([], self.client.GetFeatures())
418 def testGetOperatingSystems(self):
419 self.rapi.AddResponse("[\"beos\"]")
420 self.assertEqual(["beos"], self.client.GetOperatingSystems())
421 self.assertHandler(rlib2.R_2_os)
423 def testGetClusterTags(self):
424 self.rapi.AddResponse("[\"tag\"]")
425 self.assertEqual(["tag"], self.client.GetClusterTags())
426 self.assertHandler(rlib2.R_2_tags)
428 def testAddClusterTags(self):
429 self.rapi.AddResponse("1234")
430 self.assertEqual(1234,
431 self.client.AddClusterTags(["awesome"], dry_run=True))
432 self.assertHandler(rlib2.R_2_tags)
434 self.assertQuery("tag", ["awesome"])
436 def testDeleteClusterTags(self):
437 self.rapi.AddResponse("5107")
438 self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
440 self.assertHandler(rlib2.R_2_tags)
442 self.assertQuery("tag", ["awesome"])
444 def testGetInfo(self):
445 self.rapi.AddResponse("{}")
446 self.assertEqual({}, self.client.GetInfo())
447 self.assertHandler(rlib2.R_2_info)
449 def testGetInstances(self):
450 self.rapi.AddResponse("[]")
451 self.assertEqual([], self.client.GetInstances(bulk=True))
452 self.assertHandler(rlib2.R_2_instances)
455 def testGetInstance(self):
456 self.rapi.AddResponse("[]")
457 self.assertEqual([], self.client.GetInstance("instance"))
458 self.assertHandler(rlib2.R_2_instances_name)
459 self.assertItems(["instance"])
461 def testGetInstanceInfo(self):
462 self.rapi.AddResponse("21291")
463 self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
464 self.assertHandler(rlib2.R_2_instances_name_info)
465 self.assertItems(["inst3"])
466 self.assertQuery("static", None)
468 self.rapi.AddResponse("3428")
469 self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
470 self.assertHandler(rlib2.R_2_instances_name_info)
471 self.assertItems(["inst31"])
472 self.assertQuery("static", ["0"])
474 self.rapi.AddResponse("15665")
475 self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
476 self.assertHandler(rlib2.R_2_instances_name_info)
477 self.assertItems(["inst32"])
478 self.assertQuery("static", ["1"])
480 def testCreateInstanceOldVersion(self):
482 self.rapi.AddResponse(None, code=404)
483 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
484 "create", "inst1.example.com", "plain", [], [])
485 self.assertEqual(self.rapi.CountPending(), 0)
488 self.rapi.AddResponse(None, code=404)
489 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
490 "create", "inst1.example.com", "plain", [],
492 self.assertEqual(self.rapi.CountPending(), 0)
494 # Unsupported NIC fields
495 self.rapi.AddResponse(None, code=404)
496 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
497 "create", "inst1.example.com", "plain", [],
498 [{"x": True, "y": False}])
499 self.assertEqual(self.rapi.CountPending(), 0)
501 # Unsupported disk fields
502 self.rapi.AddResponse(None, code=404)
503 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
504 "create", "inst1.example.com", "plain",
505 [{}, {"moo": "foo",}], [{}])
506 self.assertEqual(self.rapi.CountPending(), 0)
509 self.rapi.AddResponse(None, code=404)
510 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
511 "create", "inst1.example.com", "plain", [], [{}],
513 self.assertEqual(self.rapi.CountPending(), 0)
515 self.rapi.AddResponse(None, code=404)
516 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
517 "create", "inst1.example.com", "plain", [], [{}],
519 self.assertEqual(self.rapi.CountPending(), 0)
524 [{ "mac": constants.VALUE_AUTO, }],
525 [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
531 [{ "size": 321, }, { "size": 4096, }],
534 for idx, nics in enumerate(testnics):
535 for disks in testdisks:
537 constants.BE_MEMORY: 512,
538 constants.BE_AUTO_BALANCE: False,
541 constants.HV_MIGRATION_PORT: 9876,
542 constants.HV_VNC_TLS: True,
545 self.rapi.AddResponse(None, code=404)
546 self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
547 job_id = self.client.CreateInstance("create", "inst1.example.com",
548 "plain", disks, nics,
549 pnode="node99", dry_run=True,
552 self.assertEqual(job_id, 3122617 + idx)
553 self.assertHandler(rlib2.R_2_instances)
555 self.assertEqual(self.rapi.CountPending(), 0)
557 data = serializer.LoadJson(self.rapi.GetLastRequestData())
558 self.assertEqual(data["name"], "inst1.example.com")
559 self.assertEqual(data["disk_template"], "plain")
560 self.assertEqual(data["pnode"], "node99")
561 self.assertEqual(data[constants.BE_MEMORY], 512)
562 self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
563 self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
564 self.assertEqual(data[constants.HV_VNC_TLS], True)
565 self.assertEqual(data["disks"], [disk["size"] for disk in disks])
567 def testCreateInstance(self):
568 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
569 self.rapi.AddResponse("23030")
570 job_id = self.client.CreateInstance("create", "inst1.example.com",
571 "plain", [], [], dry_run=True)
572 self.assertEqual(job_id, 23030)
573 self.assertHandler(rlib2.R_2_instances)
576 data = serializer.LoadJson(self.rapi.GetLastRequestData())
578 for field in ["dry_run", "beparams", "hvparams", "start"]:
579 self.assertFalse(field in data)
581 self.assertEqual(data["name"], "inst1.example.com")
582 self.assertEqual(data["disk_template"], "plain")
584 def testCreateInstance2(self):
585 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
586 self.rapi.AddResponse("24740")
587 job_id = self.client.CreateInstance("import", "inst2.example.com",
588 "drbd8", [{"size": 100,}],
589 [{}, {"bridge": "br1", }],
590 dry_run=False, start=True,
591 pnode="node1", snode="node9",
593 self.assertEqual(job_id, 24740)
594 self.assertHandler(rlib2.R_2_instances)
596 data = serializer.LoadJson(self.rapi.GetLastRequestData())
597 self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
598 self.assertEqual(data["name"], "inst2.example.com")
599 self.assertEqual(data["disk_template"], "drbd8")
600 self.assertEqual(data["start"], True)
601 self.assertEqual(data["ip_check"], False)
602 self.assertEqualValues(data["disks"], [{"size": 100,}])
603 self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
605 def testDeleteInstance(self):
606 self.rapi.AddResponse("1234")
607 self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
608 self.assertHandler(rlib2.R_2_instances_name)
609 self.assertItems(["instance"])
612 def testGetInstanceTags(self):
613 self.rapi.AddResponse("[]")
614 self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
615 self.assertHandler(rlib2.R_2_instances_name_tags)
616 self.assertItems(["fooinstance"])
618 def testAddInstanceTags(self):
619 self.rapi.AddResponse("1234")
620 self.assertEqual(1234,
621 self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
622 self.assertHandler(rlib2.R_2_instances_name_tags)
623 self.assertItems(["fooinstance"])
625 self.assertQuery("tag", ["awesome"])
627 def testDeleteInstanceTags(self):
628 self.rapi.AddResponse("25826")
629 self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
631 self.assertHandler(rlib2.R_2_instances_name_tags)
632 self.assertItems(["foo"])
634 self.assertQuery("tag", ["awesome"])
636 def testRebootInstance(self):
637 self.rapi.AddResponse("6146")
638 job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
639 ignore_secondaries=True, dry_run=True)
640 self.assertEqual(6146, job_id)
641 self.assertHandler(rlib2.R_2_instances_name_reboot)
642 self.assertItems(["i-bar"])
644 self.assertQuery("type", ["hard"])
645 self.assertQuery("ignore_secondaries", ["1"])
647 def testShutdownInstance(self):
648 self.rapi.AddResponse("1487")
649 self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
651 self.assertHandler(rlib2.R_2_instances_name_shutdown)
652 self.assertItems(["foo-instance"])
655 def testStartupInstance(self):
656 self.rapi.AddResponse("27149")
657 self.assertEqual(27149, self.client.StartupInstance("bar-instance",
659 self.assertHandler(rlib2.R_2_instances_name_startup)
660 self.assertItems(["bar-instance"])
663 def testReinstallInstance(self):
664 self.rapi.AddResponse(serializer.DumpJson([]))
665 self.rapi.AddResponse("19119")
666 self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
669 self.assertHandler(rlib2.R_2_instances_name_reinstall)
670 self.assertItems(["baz-instance"])
671 self.assertQuery("os", ["DOS"])
672 self.assertQuery("nostartup", ["1"])
673 self.assertEqual(self.rapi.CountPending(), 0)
675 def testReinstallInstanceNew(self):
676 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
677 self.rapi.AddResponse("25689")
678 self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
681 self.assertHandler(rlib2.R_2_instances_name_reinstall)
682 self.assertItems(["moo-instance"])
683 data = serializer.LoadJson(self.rapi.GetLastRequestData())
684 self.assertEqual(len(data), 2)
685 self.assertEqual(data["os"], "Debian")
686 self.assertEqual(data["start"], False)
687 self.assertEqual(self.rapi.CountPending(), 0)
689 def testReinstallInstanceWithOsparams1(self):
690 self.rapi.AddResponse(serializer.DumpJson([]))
691 self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
692 "doo-instance", osparams={"x": "y"})
693 self.assertEqual(self.rapi.CountPending(), 0)
695 def testReinstallInstanceWithOsparams2(self):
700 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
701 self.rapi.AddResponse("1717")
702 self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
704 self.assertHandler(rlib2.R_2_instances_name_reinstall)
705 self.assertItems(["zoo-instance"])
706 data = serializer.LoadJson(self.rapi.GetLastRequestData())
707 self.assertEqual(len(data), 2)
708 self.assertEqual(data["osparams"], osparams)
709 self.assertEqual(data["start"], True)
710 self.assertEqual(self.rapi.CountPending(), 0)
712 def testReplaceInstanceDisks(self):
713 self.rapi.AddResponse("999")
714 job_id = self.client.ReplaceInstanceDisks("instance-name",
715 disks=[0, 1], dry_run=True, iallocator="hail")
716 self.assertEqual(999, job_id)
717 self.assertHandler(rlib2.R_2_instances_name_replace_disks)
718 self.assertItems(["instance-name"])
719 self.assertQuery("disks", ["0,1"])
720 self.assertQuery("mode", ["replace_auto"])
721 self.assertQuery("iallocator", ["hail"])
724 self.rapi.AddResponse("1000")
725 job_id = self.client.ReplaceInstanceDisks("instance-bar",
726 disks=[1], mode="replace_on_secondary", remote_node="foo-node",
728 self.assertEqual(1000, job_id)
729 self.assertItems(["instance-bar"])
730 self.assertQuery("disks", ["1"])
731 self.assertQuery("remote_node", ["foo-node"])
734 self.rapi.AddResponse("5175")
735 self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
736 self.assertItems(["instance-moo"])
737 self.assertQuery("disks", None)
739 def testPrepareExport(self):
740 self.rapi.AddResponse("8326")
741 self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
742 self.assertHandler(rlib2.R_2_instances_name_prepare_export)
743 self.assertItems(["inst1"])
744 self.assertQuery("mode", ["local"])
746 def testExportInstance(self):
747 self.rapi.AddResponse("19695")
748 job_id = self.client.ExportInstance("inst2", "local", "nodeX",
750 self.assertEqual(job_id, 19695)
751 self.assertHandler(rlib2.R_2_instances_name_export)
752 self.assertItems(["inst2"])
754 data = serializer.LoadJson(self.rapi.GetLastRequestData())
755 self.assertEqual(data["mode"], "local")
756 self.assertEqual(data["destination"], "nodeX")
757 self.assertEqual(data["shutdown"], True)
759 def testMigrateInstanceDefaults(self):
760 self.rapi.AddResponse("24873")
761 job_id = self.client.MigrateInstance("inst91")
762 self.assertEqual(job_id, 24873)
763 self.assertHandler(rlib2.R_2_instances_name_migrate)
764 self.assertItems(["inst91"])
766 data = serializer.LoadJson(self.rapi.GetLastRequestData())
767 self.assertFalse(data)
769 def testMigrateInstance(self):
770 for mode in constants.HT_MIGRATION_MODES:
771 for cleanup in [False, True]:
772 self.rapi.AddResponse("31910")
773 job_id = self.client.MigrateInstance("inst289", mode=mode,
775 self.assertEqual(job_id, 31910)
776 self.assertHandler(rlib2.R_2_instances_name_migrate)
777 self.assertItems(["inst289"])
779 data = serializer.LoadJson(self.rapi.GetLastRequestData())
780 self.assertEqual(len(data), 2)
781 self.assertEqual(data["mode"], mode)
782 self.assertEqual(data["cleanup"], cleanup)
784 def testRenameInstanceDefaults(self):
785 new_name = "newnametha7euqu"
786 self.rapi.AddResponse("8791")
787 job_id = self.client.RenameInstance("inst18821", new_name)
788 self.assertEqual(job_id, 8791)
789 self.assertHandler(rlib2.R_2_instances_name_rename)
790 self.assertItems(["inst18821"])
792 data = serializer.LoadJson(self.rapi.GetLastRequestData())
793 self.assertEqualValues(data, {"new_name": new_name, })
795 def testRenameInstance(self):
796 new_name = "new-name-yiux1iin"
797 for ip_check in [False, True]:
798 for name_check in [False, True]:
799 self.rapi.AddResponse("24776")
800 job_id = self.client.RenameInstance("inst20967", new_name,
802 name_check=name_check)
803 self.assertEqual(job_id, 24776)
804 self.assertHandler(rlib2.R_2_instances_name_rename)
805 self.assertItems(["inst20967"])
807 data = serializer.LoadJson(self.rapi.GetLastRequestData())
808 self.assertEqual(len(data), 3)
809 self.assertEqual(data["new_name"], new_name)
810 self.assertEqual(data["ip_check"], ip_check)
811 self.assertEqual(data["name_check"], name_check)
813 def testGetJobs(self):
814 self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
815 ' { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
816 self.assertEqual([123, 124], self.client.GetJobs())
817 self.assertHandler(rlib2.R_2_jobs)
819 def testGetJobStatus(self):
820 self.rapi.AddResponse("{\"foo\": \"bar\"}")
821 self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
822 self.assertHandler(rlib2.R_2_jobs_id)
823 self.assertItems(["1234"])
825 def testWaitForJobChange(self):
826 fields = ["id", "summary"]
828 "job_info": [123, "something"],
832 self.rapi.AddResponse(serializer.DumpJson(expected))
833 result = self.client.WaitForJobChange(123, fields, [], -1)
834 self.assertEqualValues(expected, result)
835 self.assertHandler(rlib2.R_2_jobs_id_wait)
836 self.assertItems(["123"])
838 def testCancelJob(self):
839 self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
840 self.assertEqual([True, "Job 123 will be canceled"],
841 self.client.CancelJob(999, dry_run=True))
842 self.assertHandler(rlib2.R_2_jobs_id)
843 self.assertItems(["999"])
846 def testGetNodes(self):
847 self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
848 " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
849 self.assertEqual(["node1", "node2"], self.client.GetNodes())
850 self.assertHandler(rlib2.R_2_nodes)
852 self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
853 " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
854 self.assertEqual([{"id": "node1", "uri": "uri1"},
855 {"id": "node2", "uri": "uri2"}],
856 self.client.GetNodes(bulk=True))
857 self.assertHandler(rlib2.R_2_nodes)
860 def testGetNode(self):
861 self.rapi.AddResponse("{}")
862 self.assertEqual({}, self.client.GetNode("node-foo"))
863 self.assertHandler(rlib2.R_2_nodes_name)
864 self.assertItems(["node-foo"])
866 def testEvacuateNode(self):
867 self.rapi.AddResponse("9876")
868 job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
869 self.assertEqual(9876, job_id)
870 self.assertHandler(rlib2.R_2_nodes_name_evacuate)
871 self.assertItems(["node-1"])
872 self.assertQuery("remote_node", ["node-2"])
874 self.rapi.AddResponse("8888")
875 job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
876 self.assertEqual(8888, job_id)
877 self.assertItems(["node-3"])
878 self.assertQuery("iallocator", ["hail"])
881 self.assertRaises(client.GanetiApiError,
882 self.client.EvacuateNode,
883 "node-4", iallocator="hail", remote_node="node-5")
885 def testMigrateNode(self):
886 self.rapi.AddResponse("1111")
887 self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
888 self.assertHandler(rlib2.R_2_nodes_name_migrate)
889 self.assertItems(["node-a"])
890 self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
893 self.rapi.AddResponse("1112")
894 self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
896 self.assertHandler(rlib2.R_2_nodes_name_migrate)
897 self.assertItems(["node-a"])
898 self.assertQuery("mode", ["live"])
901 def testGetNodeRole(self):
902 self.rapi.AddResponse("\"master\"")
903 self.assertEqual("master", self.client.GetNodeRole("node-a"))
904 self.assertHandler(rlib2.R_2_nodes_name_role)
905 self.assertItems(["node-a"])
907 def testSetNodeRole(self):
908 self.rapi.AddResponse("789")
909 self.assertEqual(789,
910 self.client.SetNodeRole("node-foo", "master-candidate", force=True))
911 self.assertHandler(rlib2.R_2_nodes_name_role)
912 self.assertItems(["node-foo"])
913 self.assertQuery("force", ["1"])
914 self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
916 def testGetNodeStorageUnits(self):
917 self.rapi.AddResponse("42")
919 self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
920 self.assertHandler(rlib2.R_2_nodes_name_storage)
921 self.assertItems(["node-x"])
922 self.assertQuery("storage_type", ["lvm-pv"])
923 self.assertQuery("output_fields", ["fields"])
925 def testModifyNodeStorageUnits(self):
926 self.rapi.AddResponse("14")
928 self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
929 self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
930 self.assertItems(["node-z"])
931 self.assertQuery("storage_type", ["lvm-pv"])
932 self.assertQuery("name", ["hda"])
933 self.assertQuery("allocatable", None)
935 for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
936 self.rapi.AddResponse("7205")
937 job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
938 allocatable=allocatable)
939 self.assertEqual(7205, job_id)
940 self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
941 self.assertItems(["node-z"])
942 self.assertQuery("storage_type", ["lvm-pv"])
943 self.assertQuery("name", ["hda"])
944 self.assertQuery("allocatable", [query_allocatable])
946 def testRepairNodeStorageUnits(self):
947 self.rapi.AddResponse("99")
948 self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
950 self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
951 self.assertItems(["node-z"])
952 self.assertQuery("storage_type", ["lvm-pv"])
953 self.assertQuery("name", ["hda"])
955 def testGetNodeTags(self):
956 self.rapi.AddResponse("[\"fry\", \"bender\"]")
957 self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
958 self.assertHandler(rlib2.R_2_nodes_name_tags)
959 self.assertItems(["node-k"])
961 def testAddNodeTags(self):
962 self.rapi.AddResponse("1234")
963 self.assertEqual(1234,
964 self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
965 self.assertHandler(rlib2.R_2_nodes_name_tags)
966 self.assertItems(["node-v"])
968 self.assertQuery("tag", ["awesome"])
970 def testDeleteNodeTags(self):
971 self.rapi.AddResponse("16861")
972 self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
974 self.assertHandler(rlib2.R_2_nodes_name_tags)
975 self.assertItems(["node-w"])
977 self.assertQuery("tag", ["awesome"])
979 def testGetGroups(self):
980 groups = [{"name": "group1",
981 "uri": "/2/groups/group1",
984 "uri": "/2/groups/group2",
987 self.rapi.AddResponse(serializer.DumpJson(groups))
988 self.assertEqual(["group1", "group2"], self.client.GetGroups())
989 self.assertHandler(rlib2.R_2_groups)
991 def testGetGroupsBulk(self):
992 groups = [{"name": "group1",
993 "uri": "/2/groups/group1",
995 "node_list": ["gnt1.test",
1000 "uri": "/2/groups/group2",
1002 "node_list": ["gnt3.test",
1006 self.rapi.AddResponse(serializer.DumpJson(groups))
1008 self.assertEqual(groups, self.client.GetGroups(bulk=True))
1009 self.assertHandler(rlib2.R_2_groups)
1012 def testGetGroup(self):
1013 group = {"ctime": None,
1016 self.rapi.AddResponse(serializer.DumpJson(group))
1017 self.assertEqual({"ctime": None, "name": "default"},
1018 self.client.GetGroup("default"))
1019 self.assertHandler(rlib2.R_2_groups_name)
1020 self.assertItems(["default"])
1022 def testCreateGroup(self):
1023 self.rapi.AddResponse("12345")
1024 job_id = self.client.CreateGroup("newgroup", dry_run=True)
1025 self.assertEqual(job_id, 12345)
1026 self.assertHandler(rlib2.R_2_groups)
1029 def testDeleteGroup(self):
1030 self.rapi.AddResponse("12346")
1031 job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1032 self.assertEqual(job_id, 12346)
1033 self.assertHandler(rlib2.R_2_groups_name)
1036 def testRenameGroup(self):
1037 self.rapi.AddResponse("12347")
1038 job_id = self.client.RenameGroup("oldname", "newname")
1039 self.assertEqual(job_id, 12347)
1040 self.assertHandler(rlib2.R_2_groups_name_rename)
1042 def testModifyCluster(self):
1043 for mnh in [None, False, True]:
1044 self.rapi.AddResponse("14470")
1045 self.assertEqual(14470,
1046 self.client.ModifyCluster(maintain_node_health=mnh))
1047 self.assertHandler(rlib2.R_2_cluster_modify)
1048 self.assertItems([])
1049 data = serializer.LoadJson(self.rapi.GetLastRequestData())
1050 self.assertEqual(len(data), 1)
1051 self.assertEqual(data["maintain_node_health"], mnh)
1052 self.assertEqual(self.rapi.CountPending(), 0)
1054 def testGrowInstanceDisk(self):
1055 for idx, wait_for_sync in enumerate([None, False, True]):
1056 amount = 128 + (512 * idx)
1057 self.assertEqual(self.rapi.CountPending(), 0)
1058 self.rapi.AddResponse("30783")
1059 self.assertEqual(30783,
1060 self.client.GrowInstanceDisk("eze8ch", idx, amount,
1061 wait_for_sync=wait_for_sync))
1062 self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1063 self.assertItems(["eze8ch", str(idx)])
1064 data = serializer.LoadJson(self.rapi.GetLastRequestData())
1065 if wait_for_sync is None:
1066 self.assertEqual(len(data), 1)
1067 self.assert_("wait_for_sync" not in data)
1069 self.assertEqual(len(data), 2)
1070 self.assertEqual(data["wait_for_sync"], wait_for_sync)
1071 self.assertEqual(data["amount"], amount)
1072 self.assertEqual(self.rapi.CountPending(), 0)
1075 if __name__ == '__main__':
1076 client.UsesRapiClient(testutils.GanetiTestProgram)()