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 assertUseForce(self):
355 self.assertTrue(self.rapi.GetLastHandler().useForce())
357 def testEncodeQuery(self):
374 self.assertEqualValues(self.client._EncodeQuery(query),
378 for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
379 self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
381 def testCurlSettings(self):
382 self.rapi.AddResponse("2")
383 self.assertEqual(2, self.client.GetVersion())
384 self.assertHandler(rlib2.R_version)
386 # Signals should be disabled by default
387 self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
389 # No auth and no proxy
390 self.assertFalse(self.curl.getopt(pycurl.USERPWD))
391 self.assert_(self.curl.getopt(pycurl.PROXY) is None)
393 # Content-type is required for requests
394 headers = self.curl.getopt(pycurl.HTTPHEADER)
395 self.assert_("Content-type: application/json" in headers)
397 def testHttpError(self):
398 self.rapi.AddResponse(None, code=404)
400 self.client.GetJobStatus(15140)
401 except client.GanetiApiError, err:
402 self.assertEqual(err.code, 404)
404 self.fail("Didn't raise exception")
406 def testGetVersion(self):
407 self.rapi.AddResponse("2")
408 self.assertEqual(2, self.client.GetVersion())
409 self.assertHandler(rlib2.R_version)
411 def testGetFeatures(self):
412 for features in [[], ["foo", "bar", "baz"]]:
413 self.rapi.AddResponse(serializer.DumpJson(features))
414 self.assertEqual(features, self.client.GetFeatures())
415 self.assertHandler(rlib2.R_2_features)
417 def testGetFeaturesNotFound(self):
418 self.rapi.AddResponse(None, code=404)
419 self.assertEqual([], self.client.GetFeatures())
421 def testGetOperatingSystems(self):
422 self.rapi.AddResponse("[\"beos\"]")
423 self.assertEqual(["beos"], self.client.GetOperatingSystems())
424 self.assertHandler(rlib2.R_2_os)
426 def testGetClusterTags(self):
427 self.rapi.AddResponse("[\"tag\"]")
428 self.assertEqual(["tag"], self.client.GetClusterTags())
429 self.assertHandler(rlib2.R_2_tags)
431 def testAddClusterTags(self):
432 self.rapi.AddResponse("1234")
433 self.assertEqual(1234,
434 self.client.AddClusterTags(["awesome"], dry_run=True))
435 self.assertHandler(rlib2.R_2_tags)
437 self.assertQuery("tag", ["awesome"])
439 def testDeleteClusterTags(self):
440 self.rapi.AddResponse("5107")
441 self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
443 self.assertHandler(rlib2.R_2_tags)
445 self.assertQuery("tag", ["awesome"])
447 def testGetInfo(self):
448 self.rapi.AddResponse("{}")
449 self.assertEqual({}, self.client.GetInfo())
450 self.assertHandler(rlib2.R_2_info)
452 def testGetInstances(self):
453 self.rapi.AddResponse("[]")
454 self.assertEqual([], self.client.GetInstances(bulk=True))
455 self.assertHandler(rlib2.R_2_instances)
458 def testGetInstance(self):
459 self.rapi.AddResponse("[]")
460 self.assertEqual([], self.client.GetInstance("instance"))
461 self.assertHandler(rlib2.R_2_instances_name)
462 self.assertItems(["instance"])
464 def testGetInstanceInfo(self):
465 self.rapi.AddResponse("21291")
466 self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
467 self.assertHandler(rlib2.R_2_instances_name_info)
468 self.assertItems(["inst3"])
469 self.assertQuery("static", None)
471 self.rapi.AddResponse("3428")
472 self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
473 self.assertHandler(rlib2.R_2_instances_name_info)
474 self.assertItems(["inst31"])
475 self.assertQuery("static", ["0"])
477 self.rapi.AddResponse("15665")
478 self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
479 self.assertHandler(rlib2.R_2_instances_name_info)
480 self.assertItems(["inst32"])
481 self.assertQuery("static", ["1"])
483 def testCreateInstanceOldVersion(self):
485 self.rapi.AddResponse(None, code=404)
486 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
487 "create", "inst1.example.com", "plain", [], [])
488 self.assertEqual(self.rapi.CountPending(), 0)
491 self.rapi.AddResponse(None, code=404)
492 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
493 "create", "inst1.example.com", "plain", [],
495 self.assertEqual(self.rapi.CountPending(), 0)
497 # Unsupported NIC fields
498 self.rapi.AddResponse(None, code=404)
499 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
500 "create", "inst1.example.com", "plain", [],
501 [{"x": True, "y": False}])
502 self.assertEqual(self.rapi.CountPending(), 0)
504 # Unsupported disk fields
505 self.rapi.AddResponse(None, code=404)
506 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
507 "create", "inst1.example.com", "plain",
508 [{}, {"moo": "foo",}], [{}])
509 self.assertEqual(self.rapi.CountPending(), 0)
512 self.rapi.AddResponse(None, code=404)
513 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
514 "create", "inst1.example.com", "plain", [], [{}],
516 self.assertEqual(self.rapi.CountPending(), 0)
518 self.rapi.AddResponse(None, code=404)
519 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
520 "create", "inst1.example.com", "plain", [], [{}],
522 self.assertEqual(self.rapi.CountPending(), 0)
527 [{ "mac": constants.VALUE_AUTO, }],
528 [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
534 [{ "size": 321, }, { "size": 4096, }],
537 for idx, nics in enumerate(testnics):
538 for disks in testdisks:
540 constants.BE_MEMORY: 512,
541 constants.BE_AUTO_BALANCE: False,
544 constants.HV_MIGRATION_PORT: 9876,
545 constants.HV_VNC_TLS: True,
548 self.rapi.AddResponse(None, code=404)
549 self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
550 job_id = self.client.CreateInstance("create", "inst1.example.com",
551 "plain", disks, nics,
552 pnode="node99", dry_run=True,
555 self.assertEqual(job_id, 3122617 + idx)
556 self.assertHandler(rlib2.R_2_instances)
558 self.assertEqual(self.rapi.CountPending(), 0)
560 data = serializer.LoadJson(self.rapi.GetLastRequestData())
561 self.assertEqual(data["name"], "inst1.example.com")
562 self.assertEqual(data["disk_template"], "plain")
563 self.assertEqual(data["pnode"], "node99")
564 self.assertEqual(data[constants.BE_MEMORY], 512)
565 self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
566 self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
567 self.assertEqual(data[constants.HV_VNC_TLS], True)
568 self.assertEqual(data["disks"], [disk["size"] for disk in disks])
570 def testCreateInstance(self):
571 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
572 self.rapi.AddResponse("23030")
573 job_id = self.client.CreateInstance("create", "inst1.example.com",
574 "plain", [], [], dry_run=True)
575 self.assertEqual(job_id, 23030)
576 self.assertHandler(rlib2.R_2_instances)
579 data = serializer.LoadJson(self.rapi.GetLastRequestData())
581 for field in ["dry_run", "beparams", "hvparams", "start"]:
582 self.assertFalse(field in data)
584 self.assertEqual(data["name"], "inst1.example.com")
585 self.assertEqual(data["disk_template"], "plain")
587 def testCreateInstance2(self):
588 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
589 self.rapi.AddResponse("24740")
590 job_id = self.client.CreateInstance("import", "inst2.example.com",
591 "drbd8", [{"size": 100,}],
592 [{}, {"bridge": "br1", }],
593 dry_run=False, start=True,
594 pnode="node1", snode="node9",
596 self.assertEqual(job_id, 24740)
597 self.assertHandler(rlib2.R_2_instances)
599 data = serializer.LoadJson(self.rapi.GetLastRequestData())
600 self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
601 self.assertEqual(data["name"], "inst2.example.com")
602 self.assertEqual(data["disk_template"], "drbd8")
603 self.assertEqual(data["start"], True)
604 self.assertEqual(data["ip_check"], False)
605 self.assertEqualValues(data["disks"], [{"size": 100,}])
606 self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
608 def testDeleteInstance(self):
609 self.rapi.AddResponse("1234")
610 self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
611 self.assertHandler(rlib2.R_2_instances_name)
612 self.assertItems(["instance"])
615 def testGetInstanceTags(self):
616 self.rapi.AddResponse("[]")
617 self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
618 self.assertHandler(rlib2.R_2_instances_name_tags)
619 self.assertItems(["fooinstance"])
621 def testAddInstanceTags(self):
622 self.rapi.AddResponse("1234")
623 self.assertEqual(1234,
624 self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
625 self.assertHandler(rlib2.R_2_instances_name_tags)
626 self.assertItems(["fooinstance"])
628 self.assertQuery("tag", ["awesome"])
630 def testDeleteInstanceTags(self):
631 self.rapi.AddResponse("25826")
632 self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
634 self.assertHandler(rlib2.R_2_instances_name_tags)
635 self.assertItems(["foo"])
637 self.assertQuery("tag", ["awesome"])
639 def testRebootInstance(self):
640 self.rapi.AddResponse("6146")
641 job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
642 ignore_secondaries=True, dry_run=True)
643 self.assertEqual(6146, job_id)
644 self.assertHandler(rlib2.R_2_instances_name_reboot)
645 self.assertItems(["i-bar"])
647 self.assertQuery("type", ["hard"])
648 self.assertQuery("ignore_secondaries", ["1"])
650 def testShutdownInstance(self):
651 self.rapi.AddResponse("1487")
652 self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
654 self.assertHandler(rlib2.R_2_instances_name_shutdown)
655 self.assertItems(["foo-instance"])
658 def testStartupInstance(self):
659 self.rapi.AddResponse("27149")
660 self.assertEqual(27149, self.client.StartupInstance("bar-instance",
662 self.assertHandler(rlib2.R_2_instances_name_startup)
663 self.assertItems(["bar-instance"])
666 def testReinstallInstance(self):
667 self.rapi.AddResponse(serializer.DumpJson([]))
668 self.rapi.AddResponse("19119")
669 self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
672 self.assertHandler(rlib2.R_2_instances_name_reinstall)
673 self.assertItems(["baz-instance"])
674 self.assertQuery("os", ["DOS"])
675 self.assertQuery("nostartup", ["1"])
676 self.assertEqual(self.rapi.CountPending(), 0)
678 def testReinstallInstanceNew(self):
679 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
680 self.rapi.AddResponse("25689")
681 self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
684 self.assertHandler(rlib2.R_2_instances_name_reinstall)
685 self.assertItems(["moo-instance"])
686 data = serializer.LoadJson(self.rapi.GetLastRequestData())
687 self.assertEqual(len(data), 2)
688 self.assertEqual(data["os"], "Debian")
689 self.assertEqual(data["start"], False)
690 self.assertEqual(self.rapi.CountPending(), 0)
692 def testReinstallInstanceWithOsparams1(self):
693 self.rapi.AddResponse(serializer.DumpJson([]))
694 self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
695 "doo-instance", osparams={"x": "y"})
696 self.assertEqual(self.rapi.CountPending(), 0)
698 def testReinstallInstanceWithOsparams2(self):
703 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
704 self.rapi.AddResponse("1717")
705 self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
707 self.assertHandler(rlib2.R_2_instances_name_reinstall)
708 self.assertItems(["zoo-instance"])
709 data = serializer.LoadJson(self.rapi.GetLastRequestData())
710 self.assertEqual(len(data), 2)
711 self.assertEqual(data["osparams"], osparams)
712 self.assertEqual(data["start"], True)
713 self.assertEqual(self.rapi.CountPending(), 0)
715 def testReplaceInstanceDisks(self):
716 self.rapi.AddResponse("999")
717 job_id = self.client.ReplaceInstanceDisks("instance-name",
718 disks=[0, 1], dry_run=True, iallocator="hail")
719 self.assertEqual(999, job_id)
720 self.assertHandler(rlib2.R_2_instances_name_replace_disks)
721 self.assertItems(["instance-name"])
722 self.assertQuery("disks", ["0,1"])
723 self.assertQuery("mode", ["replace_auto"])
724 self.assertQuery("iallocator", ["hail"])
727 self.rapi.AddResponse("1000")
728 job_id = self.client.ReplaceInstanceDisks("instance-bar",
729 disks=[1], mode="replace_on_secondary", remote_node="foo-node",
731 self.assertEqual(1000, job_id)
732 self.assertItems(["instance-bar"])
733 self.assertQuery("disks", ["1"])
734 self.assertQuery("remote_node", ["foo-node"])
737 self.rapi.AddResponse("5175")
738 self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
739 self.assertItems(["instance-moo"])
740 self.assertQuery("disks", None)
742 def testPrepareExport(self):
743 self.rapi.AddResponse("8326")
744 self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
745 self.assertHandler(rlib2.R_2_instances_name_prepare_export)
746 self.assertItems(["inst1"])
747 self.assertQuery("mode", ["local"])
749 def testExportInstance(self):
750 self.rapi.AddResponse("19695")
751 job_id = self.client.ExportInstance("inst2", "local", "nodeX",
753 self.assertEqual(job_id, 19695)
754 self.assertHandler(rlib2.R_2_instances_name_export)
755 self.assertItems(["inst2"])
757 data = serializer.LoadJson(self.rapi.GetLastRequestData())
758 self.assertEqual(data["mode"], "local")
759 self.assertEqual(data["destination"], "nodeX")
760 self.assertEqual(data["shutdown"], True)
762 def testMigrateInstanceDefaults(self):
763 self.rapi.AddResponse("24873")
764 job_id = self.client.MigrateInstance("inst91")
765 self.assertEqual(job_id, 24873)
766 self.assertHandler(rlib2.R_2_instances_name_migrate)
767 self.assertItems(["inst91"])
769 data = serializer.LoadJson(self.rapi.GetLastRequestData())
770 self.assertFalse(data)
772 def testMigrateInstance(self):
773 for mode in constants.HT_MIGRATION_MODES:
774 for cleanup in [False, True]:
775 self.rapi.AddResponse("31910")
776 job_id = self.client.MigrateInstance("inst289", mode=mode,
778 self.assertEqual(job_id, 31910)
779 self.assertHandler(rlib2.R_2_instances_name_migrate)
780 self.assertItems(["inst289"])
782 data = serializer.LoadJson(self.rapi.GetLastRequestData())
783 self.assertEqual(len(data), 2)
784 self.assertEqual(data["mode"], mode)
785 self.assertEqual(data["cleanup"], cleanup)
787 def testRenameInstanceDefaults(self):
788 new_name = "newnametha7euqu"
789 self.rapi.AddResponse("8791")
790 job_id = self.client.RenameInstance("inst18821", new_name)
791 self.assertEqual(job_id, 8791)
792 self.assertHandler(rlib2.R_2_instances_name_rename)
793 self.assertItems(["inst18821"])
795 data = serializer.LoadJson(self.rapi.GetLastRequestData())
796 self.assertEqualValues(data, {"new_name": new_name, })
798 def testRenameInstance(self):
799 new_name = "new-name-yiux1iin"
800 for ip_check in [False, True]:
801 for name_check in [False, True]:
802 self.rapi.AddResponse("24776")
803 job_id = self.client.RenameInstance("inst20967", new_name,
805 name_check=name_check)
806 self.assertEqual(job_id, 24776)
807 self.assertHandler(rlib2.R_2_instances_name_rename)
808 self.assertItems(["inst20967"])
810 data = serializer.LoadJson(self.rapi.GetLastRequestData())
811 self.assertEqual(len(data), 3)
812 self.assertEqual(data["new_name"], new_name)
813 self.assertEqual(data["ip_check"], ip_check)
814 self.assertEqual(data["name_check"], name_check)
816 def testGetJobs(self):
817 self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
818 ' { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
819 self.assertEqual([123, 124], self.client.GetJobs())
820 self.assertHandler(rlib2.R_2_jobs)
822 def testGetJobStatus(self):
823 self.rapi.AddResponse("{\"foo\": \"bar\"}")
824 self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
825 self.assertHandler(rlib2.R_2_jobs_id)
826 self.assertItems(["1234"])
828 def testWaitForJobChange(self):
829 fields = ["id", "summary"]
831 "job_info": [123, "something"],
835 self.rapi.AddResponse(serializer.DumpJson(expected))
836 result = self.client.WaitForJobChange(123, fields, [], -1)
837 self.assertEqualValues(expected, result)
838 self.assertHandler(rlib2.R_2_jobs_id_wait)
839 self.assertItems(["123"])
841 def testCancelJob(self):
842 self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
843 self.assertEqual([True, "Job 123 will be canceled"],
844 self.client.CancelJob(999, dry_run=True))
845 self.assertHandler(rlib2.R_2_jobs_id)
846 self.assertItems(["999"])
849 def testGetNodes(self):
850 self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
851 " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
852 self.assertEqual(["node1", "node2"], self.client.GetNodes())
853 self.assertHandler(rlib2.R_2_nodes)
855 self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
856 " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
857 self.assertEqual([{"id": "node1", "uri": "uri1"},
858 {"id": "node2", "uri": "uri2"}],
859 self.client.GetNodes(bulk=True))
860 self.assertHandler(rlib2.R_2_nodes)
863 def testGetNode(self):
864 self.rapi.AddResponse("{}")
865 self.assertEqual({}, self.client.GetNode("node-foo"))
866 self.assertHandler(rlib2.R_2_nodes_name)
867 self.assertItems(["node-foo"])
869 def testEvacuateNode(self):
870 self.rapi.AddResponse("9876")
871 job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
872 self.assertEqual(9876, job_id)
873 self.assertHandler(rlib2.R_2_nodes_name_evacuate)
874 self.assertItems(["node-1"])
875 self.assertQuery("remote_node", ["node-2"])
877 self.rapi.AddResponse("8888")
878 job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
879 self.assertEqual(8888, job_id)
880 self.assertItems(["node-3"])
881 self.assertQuery("iallocator", ["hail"])
884 self.assertRaises(client.GanetiApiError,
885 self.client.EvacuateNode,
886 "node-4", iallocator="hail", remote_node="node-5")
888 def testMigrateNode(self):
889 self.rapi.AddResponse("1111")
890 self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
891 self.assertHandler(rlib2.R_2_nodes_name_migrate)
892 self.assertItems(["node-a"])
893 self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
896 self.rapi.AddResponse("1112")
897 self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
899 self.assertHandler(rlib2.R_2_nodes_name_migrate)
900 self.assertItems(["node-a"])
901 self.assertQuery("mode", ["live"])
904 def testGetNodeRole(self):
905 self.rapi.AddResponse("\"master\"")
906 self.assertEqual("master", self.client.GetNodeRole("node-a"))
907 self.assertHandler(rlib2.R_2_nodes_name_role)
908 self.assertItems(["node-a"])
910 def testSetNodeRole(self):
911 self.rapi.AddResponse("789")
912 self.assertEqual(789,
913 self.client.SetNodeRole("node-foo", "master-candidate", force=True))
914 self.assertHandler(rlib2.R_2_nodes_name_role)
915 self.assertItems(["node-foo"])
916 self.assertQuery("force", ["1"])
917 self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
919 def testGetNodeStorageUnits(self):
920 self.rapi.AddResponse("42")
922 self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
923 self.assertHandler(rlib2.R_2_nodes_name_storage)
924 self.assertItems(["node-x"])
925 self.assertQuery("storage_type", ["lvm-pv"])
926 self.assertQuery("output_fields", ["fields"])
928 def testModifyNodeStorageUnits(self):
929 self.rapi.AddResponse("14")
931 self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
932 self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
933 self.assertItems(["node-z"])
934 self.assertQuery("storage_type", ["lvm-pv"])
935 self.assertQuery("name", ["hda"])
936 self.assertQuery("allocatable", None)
938 for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
939 self.rapi.AddResponse("7205")
940 job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
941 allocatable=allocatable)
942 self.assertEqual(7205, job_id)
943 self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
944 self.assertItems(["node-z"])
945 self.assertQuery("storage_type", ["lvm-pv"])
946 self.assertQuery("name", ["hda"])
947 self.assertQuery("allocatable", [query_allocatable])
949 def testRepairNodeStorageUnits(self):
950 self.rapi.AddResponse("99")
951 self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
953 self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
954 self.assertItems(["node-z"])
955 self.assertQuery("storage_type", ["lvm-pv"])
956 self.assertQuery("name", ["hda"])
958 def testGetNodeTags(self):
959 self.rapi.AddResponse("[\"fry\", \"bender\"]")
960 self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
961 self.assertHandler(rlib2.R_2_nodes_name_tags)
962 self.assertItems(["node-k"])
964 def testAddNodeTags(self):
965 self.rapi.AddResponse("1234")
966 self.assertEqual(1234,
967 self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
968 self.assertHandler(rlib2.R_2_nodes_name_tags)
969 self.assertItems(["node-v"])
971 self.assertQuery("tag", ["awesome"])
973 def testDeleteNodeTags(self):
974 self.rapi.AddResponse("16861")
975 self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
977 self.assertHandler(rlib2.R_2_nodes_name_tags)
978 self.assertItems(["node-w"])
980 self.assertQuery("tag", ["awesome"])
982 def testGetGroups(self):
983 groups = [{"name": "group1",
984 "uri": "/2/groups/group1",
987 "uri": "/2/groups/group2",
990 self.rapi.AddResponse(serializer.DumpJson(groups))
991 self.assertEqual(["group1", "group2"], self.client.GetGroups())
992 self.assertHandler(rlib2.R_2_groups)
994 def testGetGroupsBulk(self):
995 groups = [{"name": "group1",
996 "uri": "/2/groups/group1",
998 "node_list": ["gnt1.test",
1003 "uri": "/2/groups/group2",
1005 "node_list": ["gnt3.test",
1009 self.rapi.AddResponse(serializer.DumpJson(groups))
1011 self.assertEqual(groups, self.client.GetGroups(bulk=True))
1012 self.assertHandler(rlib2.R_2_groups)
1015 def testGetGroup(self):
1016 group = {"ctime": None,
1019 self.rapi.AddResponse(serializer.DumpJson(group))
1020 self.assertEqual({"ctime": None, "name": "default"},
1021 self.client.GetGroup("default"))
1022 self.assertHandler(rlib2.R_2_groups_name)
1023 self.assertItems(["default"])
1025 def testCreateGroup(self):
1026 self.rapi.AddResponse("12345")
1027 job_id = self.client.CreateGroup("newgroup", dry_run=True)
1028 self.assertEqual(job_id, 12345)
1029 self.assertHandler(rlib2.R_2_groups)
1032 def testDeleteGroup(self):
1033 self.rapi.AddResponse("12346")
1034 job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1035 self.assertEqual(job_id, 12346)
1036 self.assertHandler(rlib2.R_2_groups_name)
1039 def testRenameGroup(self):
1040 self.rapi.AddResponse("12347")
1041 job_id = self.client.RenameGroup("oldname", "newname")
1042 self.assertEqual(job_id, 12347)
1043 self.assertHandler(rlib2.R_2_groups_name_rename)
1045 def testModifyGroup(self):
1046 self.rapi.AddResponse("12348")
1047 job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1048 self.assertEqual(job_id, 12348)
1049 self.assertHandler(rlib2.R_2_groups_name_modify)
1051 def testAssignGroupNodes(self):
1052 self.rapi.AddResponse("12349")
1053 job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1054 force=True, dry_run=True)
1055 self.assertEqual(job_id, 12349)
1056 self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1058 self.assertUseForce()
1060 def testModifyInstance(self):
1061 self.rapi.AddResponse("23681")
1062 job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1063 self.assertEqual(job_id, 23681)
1064 self.assertItems(["inst7210"])
1065 self.assertHandler(rlib2.R_2_instances_name_modify)
1066 self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1067 { "os_name": "linux", })
1069 def testModifyCluster(self):
1070 for mnh in [None, False, True]:
1071 self.rapi.AddResponse("14470")
1072 self.assertEqual(14470,
1073 self.client.ModifyCluster(maintain_node_health=mnh))
1074 self.assertHandler(rlib2.R_2_cluster_modify)
1075 self.assertItems([])
1076 data = serializer.LoadJson(self.rapi.GetLastRequestData())
1077 self.assertEqual(len(data), 1)
1078 self.assertEqual(data["maintain_node_health"], mnh)
1079 self.assertEqual(self.rapi.CountPending(), 0)
1081 def testRedistributeConfig(self):
1082 self.rapi.AddResponse("3364")
1083 job_id = self.client.RedistributeConfig()
1084 self.assertEqual(job_id, 3364)
1085 self.assertItems([])
1086 self.assertHandler(rlib2.R_2_redist_config)
1088 def testActivateInstanceDisks(self):
1089 self.rapi.AddResponse("23547")
1090 job_id = self.client.ActivateInstanceDisks("inst28204")
1091 self.assertEqual(job_id, 23547)
1092 self.assertItems(["inst28204"])
1093 self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1094 self.assertFalse(self.rapi.GetLastHandler().queryargs)
1096 def testActivateInstanceDisksIgnoreSize(self):
1097 self.rapi.AddResponse("11044")
1098 job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1099 self.assertEqual(job_id, 11044)
1100 self.assertItems(["inst28204"])
1101 self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1102 self.assertQuery("ignore_size", ["1"])
1104 def testDeactivateInstanceDisks(self):
1105 self.rapi.AddResponse("14591")
1106 job_id = self.client.DeactivateInstanceDisks("inst28234")
1107 self.assertEqual(job_id, 14591)
1108 self.assertItems(["inst28234"])
1109 self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1110 self.assertFalse(self.rapi.GetLastHandler().queryargs)
1112 def testGrowInstanceDisk(self):
1113 for idx, wait_for_sync in enumerate([None, False, True]):
1114 amount = 128 + (512 * idx)
1115 self.assertEqual(self.rapi.CountPending(), 0)
1116 self.rapi.AddResponse("30783")
1117 self.assertEqual(30783,
1118 self.client.GrowInstanceDisk("eze8ch", idx, amount,
1119 wait_for_sync=wait_for_sync))
1120 self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1121 self.assertItems(["eze8ch", str(idx)])
1122 data = serializer.LoadJson(self.rapi.GetLastRequestData())
1123 if wait_for_sync is None:
1124 self.assertEqual(len(data), 1)
1125 self.assert_("wait_for_sync" not in data)
1127 self.assertEqual(len(data), 2)
1128 self.assertEqual(data["wait_for_sync"], wait_for_sync)
1129 self.assertEqual(data["amount"], amount)
1130 self.assertEqual(self.rapi.CountPending(), 0)
1133 if __name__ == '__main__':
1134 client.UsesRapiClient(testutils.GanetiTestProgram)()