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
33 from ganeti import utils
35 from ganeti.rapi import connector
36 from ganeti.rapi import rlib2
37 from ganeti.rapi import client
42 _URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
44 # List of resource handlers which aren't used by the RAPI client
50 # Global variable for collecting used handlers
54 def _GetPathFromUri(uri):
55 """Gets the path and query from a URI.
58 match = _URI_RE.match(uri)
60 return match.groupdict()["path"]
66 def __init__(self, rapi):
71 def setopt(self, opt, value):
72 self._opts[opt] = value
74 def getopt(self, opt):
75 return self._opts.get(opt)
77 def unsetopt(self, opt):
78 self._opts.pop(opt, None)
80 def getinfo(self, info):
81 return self._info[info]
84 method = self._opts[pycurl.CUSTOMREQUEST]
85 url = self._opts[pycurl.URL]
86 request_body = self._opts[pycurl.POSTFIELDS]
87 writefn = self._opts[pycurl.WRITEFUNCTION]
89 path = _GetPathFromUri(url)
90 (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
92 self._info[pycurl.RESPONSE_CODE] = code
93 if resp_body is not None:
97 class RapiMock(object):
99 self._mapper = connector.Mapper()
101 self._last_handler = None
102 self._last_req_data = None
104 def AddResponse(self, response, code=200):
105 self._responses.insert(0, (code, response))
107 def CountPending(self):
108 return len(self._responses)
110 def GetLastHandler(self):
111 return self._last_handler
113 def GetLastRequestData(self):
114 return self._last_req_data
116 def FetchResponse(self, path, method, request_body):
117 self._last_req_data = request_body
120 (handler_cls, items, args) = self._mapper.getController(path)
122 # Record handler as used
123 _used_handlers.add(handler_cls)
125 self._last_handler = handler_cls(items, args, None)
126 if not hasattr(self._last_handler, method.upper()):
127 raise http.HttpNotImplemented(message="Method not implemented")
129 except http.HttpException, ex:
131 response = ex.message
133 if not self._responses:
134 raise Exception("No responses")
136 (code, response) = self._responses.pop()
138 return code, response
141 class TestConstants(unittest.TestCase):
143 self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
144 self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
145 self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
146 self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
147 self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
148 self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
149 self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
152 class RapiMockTest(unittest.TestCase):
156 self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
157 self.assertEqual((501, "Method not implemented"),
158 rapi.FetchResponse("/version", "POST", None))
159 rapi.AddResponse("2")
160 code, response = rapi.FetchResponse("/version", "GET", None)
161 self.assertEqual(200, code)
162 self.assertEqual("2", response)
163 self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
166 def _FakeNoSslPycurlVersion():
167 # Note: incomplete version tuple
168 return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
171 def _FakeFancySslPycurlVersion():
172 # Note: incomplete version tuple
173 return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
176 def _FakeOpenSslPycurlVersion():
177 # Note: incomplete version tuple
178 return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
181 def _FakeGnuTlsPycurlVersion():
182 # Note: incomplete version tuple
183 return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
186 class TestExtendedConfig(unittest.TestCase):
188 cl = client.GanetiRapiClient("master.example.com",
189 username="user", password="pw",
190 curl_factory=lambda: FakeCurl(RapiMock()))
192 curl = cl._CreateCurl()
193 self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
194 self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
196 def testInvalidAuth(self):
198 self.assertRaises(client.Error, client.GanetiRapiClient,
199 "master-a.example.com", password="pw")
201 self.assertRaises(client.Error, client.GanetiRapiClient,
202 "master-b.example.com", username="user")
204 def testCertVerifyInvalidCombinations(self):
205 self.assertRaises(client.Error, client.GenericCurlConfig,
206 use_curl_cabundle=True, cafile="cert1.pem")
207 self.assertRaises(client.Error, client.GenericCurlConfig,
208 use_curl_cabundle=True, capath="certs/")
209 self.assertRaises(client.Error, client.GenericCurlConfig,
210 use_curl_cabundle=True,
211 cafile="cert1.pem", capath="certs/")
213 def testProxySignalVerifyHostname(self):
214 for use_gnutls in [False, True]:
216 pcverfn = _FakeGnuTlsPycurlVersion
218 pcverfn = _FakeOpenSslPycurlVersion
220 for proxy in ["", "http://127.0.0.1:1234"]:
221 for use_signal in [False, True]:
222 for verify_hostname in [False, True]:
223 cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
224 verify_hostname=verify_hostname,
225 _pycurl_version_fn=pcverfn)
227 curl_factory = lambda: FakeCurl(RapiMock())
228 cl = client.GanetiRapiClient("master.example.com",
229 curl_config_fn=cfgfn,
230 curl_factory=curl_factory)
232 curl = cl._CreateCurl()
233 self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
234 self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
237 self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
239 self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
241 def testNoCertVerify(self):
242 cfgfn = client.GenericCurlConfig()
244 curl_factory = lambda: FakeCurl(RapiMock())
245 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
246 curl_factory=curl_factory)
248 curl = cl._CreateCurl()
249 self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
250 self.assertFalse(curl.getopt(pycurl.CAINFO))
251 self.assertFalse(curl.getopt(pycurl.CAPATH))
253 def testCertVerifyCurlBundle(self):
254 cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
256 curl_factory = lambda: FakeCurl(RapiMock())
257 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
258 curl_factory=curl_factory)
260 curl = cl._CreateCurl()
261 self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
262 self.assertFalse(curl.getopt(pycurl.CAINFO))
263 self.assertFalse(curl.getopt(pycurl.CAPATH))
265 def testCertVerifyCafile(self):
266 mycert = "/tmp/some/UNUSED/cert/file.pem"
267 cfgfn = client.GenericCurlConfig(cafile=mycert)
269 curl_factory = lambda: FakeCurl(RapiMock())
270 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
271 curl_factory=curl_factory)
273 curl = cl._CreateCurl()
274 self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
275 self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
276 self.assertFalse(curl.getopt(pycurl.CAPATH))
278 def testCertVerifyCapath(self):
279 certdir = "/tmp/some/UNUSED/cert/directory"
280 pcverfn = _FakeOpenSslPycurlVersion
281 cfgfn = client.GenericCurlConfig(capath=certdir,
282 _pycurl_version_fn=pcverfn)
284 curl_factory = lambda: FakeCurl(RapiMock())
285 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
286 curl_factory=curl_factory)
288 curl = cl._CreateCurl()
289 self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
290 self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
291 self.assertFalse(curl.getopt(pycurl.CAINFO))
293 def testCertVerifyCapathGnuTls(self):
294 certdir = "/tmp/some/UNUSED/cert/directory"
295 pcverfn = _FakeGnuTlsPycurlVersion
296 cfgfn = client.GenericCurlConfig(capath=certdir,
297 _pycurl_version_fn=pcverfn)
299 curl_factory = lambda: FakeCurl(RapiMock())
300 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
301 curl_factory=curl_factory)
303 self.assertRaises(client.Error, cl._CreateCurl)
305 def testCertVerifyNoSsl(self):
306 certdir = "/tmp/some/UNUSED/cert/directory"
307 pcverfn = _FakeNoSslPycurlVersion
308 cfgfn = client.GenericCurlConfig(capath=certdir,
309 _pycurl_version_fn=pcverfn)
311 curl_factory = lambda: FakeCurl(RapiMock())
312 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
313 curl_factory=curl_factory)
315 self.assertRaises(client.Error, cl._CreateCurl)
317 def testCertVerifyFancySsl(self):
318 certdir = "/tmp/some/UNUSED/cert/directory"
319 pcverfn = _FakeFancySslPycurlVersion
320 cfgfn = client.GenericCurlConfig(capath=certdir,
321 _pycurl_version_fn=pcverfn)
323 curl_factory = lambda: FakeCurl(RapiMock())
324 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
325 curl_factory=curl_factory)
327 self.assertRaises(NotImplementedError, cl._CreateCurl)
329 def testCertVerifyCapath(self):
330 for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
331 for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
332 cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
335 curl_factory = lambda: FakeCurl(RapiMock())
336 cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
337 curl_factory=curl_factory)
339 curl = cl._CreateCurl()
340 self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
341 self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
344 class GanetiRapiClientTests(testutils.GanetiTestCase):
346 testutils.GanetiTestCase.setUp(self)
348 self.rapi = RapiMock()
349 self.curl = FakeCurl(self.rapi)
350 self.client = client.GanetiRapiClient("master.example.com",
351 curl_factory=lambda: self.curl)
353 def assertHandler(self, handler_cls):
354 self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
356 def assertQuery(self, key, value):
357 self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
359 def assertItems(self, items):
360 self.assertEqual(items, self.rapi.GetLastHandler().items)
362 def assertBulk(self):
363 self.assertTrue(self.rapi.GetLastHandler().useBulk())
365 def assertDryRun(self):
366 self.assertTrue(self.rapi.GetLastHandler().dryRun())
368 def assertUseForce(self):
369 self.assertTrue(self.rapi.GetLastHandler().useForce())
371 def testEncodeQuery(self):
388 self.assertEqualValues(self.client._EncodeQuery(query),
392 for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
393 self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
395 def testCurlSettings(self):
396 self.rapi.AddResponse("2")
397 self.assertEqual(2, self.client.GetVersion())
398 self.assertHandler(rlib2.R_version)
400 # Signals should be disabled by default
401 self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
403 # No auth and no proxy
404 self.assertFalse(self.curl.getopt(pycurl.USERPWD))
405 self.assert_(self.curl.getopt(pycurl.PROXY) is None)
407 # Content-type is required for requests
408 headers = self.curl.getopt(pycurl.HTTPHEADER)
409 self.assert_("Content-type: application/json" in headers)
411 def testHttpError(self):
412 self.rapi.AddResponse(None, code=404)
414 self.client.GetJobStatus(15140)
415 except client.GanetiApiError, err:
416 self.assertEqual(err.code, 404)
418 self.fail("Didn't raise exception")
420 def testGetVersion(self):
421 self.rapi.AddResponse("2")
422 self.assertEqual(2, self.client.GetVersion())
423 self.assertHandler(rlib2.R_version)
425 def testGetFeatures(self):
426 for features in [[], ["foo", "bar", "baz"]]:
427 self.rapi.AddResponse(serializer.DumpJson(features))
428 self.assertEqual(features, self.client.GetFeatures())
429 self.assertHandler(rlib2.R_2_features)
431 def testGetFeaturesNotFound(self):
432 self.rapi.AddResponse(None, code=404)
433 self.assertEqual([], self.client.GetFeatures())
435 def testGetOperatingSystems(self):
436 self.rapi.AddResponse("[\"beos\"]")
437 self.assertEqual(["beos"], self.client.GetOperatingSystems())
438 self.assertHandler(rlib2.R_2_os)
440 def testGetClusterTags(self):
441 self.rapi.AddResponse("[\"tag\"]")
442 self.assertEqual(["tag"], self.client.GetClusterTags())
443 self.assertHandler(rlib2.R_2_tags)
445 def testAddClusterTags(self):
446 self.rapi.AddResponse("1234")
447 self.assertEqual(1234,
448 self.client.AddClusterTags(["awesome"], dry_run=True))
449 self.assertHandler(rlib2.R_2_tags)
451 self.assertQuery("tag", ["awesome"])
453 def testDeleteClusterTags(self):
454 self.rapi.AddResponse("5107")
455 self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
457 self.assertHandler(rlib2.R_2_tags)
459 self.assertQuery("tag", ["awesome"])
461 def testGetInfo(self):
462 self.rapi.AddResponse("{}")
463 self.assertEqual({}, self.client.GetInfo())
464 self.assertHandler(rlib2.R_2_info)
466 def testGetInstances(self):
467 self.rapi.AddResponse("[]")
468 self.assertEqual([], self.client.GetInstances(bulk=True))
469 self.assertHandler(rlib2.R_2_instances)
472 def testGetInstance(self):
473 self.rapi.AddResponse("[]")
474 self.assertEqual([], self.client.GetInstance("instance"))
475 self.assertHandler(rlib2.R_2_instances_name)
476 self.assertItems(["instance"])
478 def testGetInstanceInfo(self):
479 self.rapi.AddResponse("21291")
480 self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
481 self.assertHandler(rlib2.R_2_instances_name_info)
482 self.assertItems(["inst3"])
483 self.assertQuery("static", None)
485 self.rapi.AddResponse("3428")
486 self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
487 self.assertHandler(rlib2.R_2_instances_name_info)
488 self.assertItems(["inst31"])
489 self.assertQuery("static", ["0"])
491 self.rapi.AddResponse("15665")
492 self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
493 self.assertHandler(rlib2.R_2_instances_name_info)
494 self.assertItems(["inst32"])
495 self.assertQuery("static", ["1"])
497 def testCreateInstanceOldVersion(self):
499 self.rapi.AddResponse(None, code=404)
500 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
501 "create", "inst1.example.com", "plain", [], [])
502 self.assertEqual(self.rapi.CountPending(), 0)
505 self.rapi.AddResponse(None, code=404)
506 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
507 "create", "inst1.example.com", "plain", [],
509 self.assertEqual(self.rapi.CountPending(), 0)
511 # Unsupported NIC fields
512 self.rapi.AddResponse(None, code=404)
513 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
514 "create", "inst1.example.com", "plain", [],
515 [{"x": True, "y": False}])
516 self.assertEqual(self.rapi.CountPending(), 0)
518 # Unsupported disk fields
519 self.rapi.AddResponse(None, code=404)
520 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
521 "create", "inst1.example.com", "plain",
522 [{}, {"moo": "foo",}], [{}])
523 self.assertEqual(self.rapi.CountPending(), 0)
526 self.rapi.AddResponse(None, code=404)
527 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
528 "create", "inst1.example.com", "plain", [], [{}],
530 self.assertEqual(self.rapi.CountPending(), 0)
532 self.rapi.AddResponse(None, code=404)
533 self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
534 "create", "inst1.example.com", "plain", [], [{}],
536 self.assertEqual(self.rapi.CountPending(), 0)
541 [{ "mac": constants.VALUE_AUTO, }],
542 [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
548 [{ "size": 321, }, { "size": 4096, }],
551 for idx, nics in enumerate(testnics):
552 for disks in testdisks:
554 constants.BE_MEMORY: 512,
555 constants.BE_AUTO_BALANCE: False,
558 constants.HV_MIGRATION_PORT: 9876,
559 constants.HV_VNC_TLS: True,
562 self.rapi.AddResponse(None, code=404)
563 self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
564 job_id = self.client.CreateInstance("create", "inst1.example.com",
565 "plain", disks, nics,
566 pnode="node99", dry_run=True,
569 self.assertEqual(job_id, 3122617 + idx)
570 self.assertHandler(rlib2.R_2_instances)
572 self.assertEqual(self.rapi.CountPending(), 0)
574 data = serializer.LoadJson(self.rapi.GetLastRequestData())
575 self.assertEqual(data["name"], "inst1.example.com")
576 self.assertEqual(data["disk_template"], "plain")
577 self.assertEqual(data["pnode"], "node99")
578 self.assertEqual(data[constants.BE_MEMORY], 512)
579 self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
580 self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
581 self.assertEqual(data[constants.HV_VNC_TLS], True)
582 self.assertEqual(data["disks"], [disk["size"] for disk in disks])
584 def testCreateInstance(self):
585 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
586 self.rapi.AddResponse("23030")
587 job_id = self.client.CreateInstance("create", "inst1.example.com",
588 "plain", [], [], dry_run=True)
589 self.assertEqual(job_id, 23030)
590 self.assertHandler(rlib2.R_2_instances)
593 data = serializer.LoadJson(self.rapi.GetLastRequestData())
595 for field in ["dry_run", "beparams", "hvparams", "start"]:
596 self.assertFalse(field in data)
598 self.assertEqual(data["name"], "inst1.example.com")
599 self.assertEqual(data["disk_template"], "plain")
601 def testCreateInstance2(self):
602 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
603 self.rapi.AddResponse("24740")
604 job_id = self.client.CreateInstance("import", "inst2.example.com",
605 "drbd8", [{"size": 100,}],
606 [{}, {"bridge": "br1", }],
607 dry_run=False, start=True,
608 pnode="node1", snode="node9",
610 self.assertEqual(job_id, 24740)
611 self.assertHandler(rlib2.R_2_instances)
613 data = serializer.LoadJson(self.rapi.GetLastRequestData())
614 self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
615 self.assertEqual(data["name"], "inst2.example.com")
616 self.assertEqual(data["disk_template"], "drbd8")
617 self.assertEqual(data["start"], True)
618 self.assertEqual(data["ip_check"], False)
619 self.assertEqualValues(data["disks"], [{"size": 100,}])
620 self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
622 def testDeleteInstance(self):
623 self.rapi.AddResponse("1234")
624 self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
625 self.assertHandler(rlib2.R_2_instances_name)
626 self.assertItems(["instance"])
629 def testGetInstanceTags(self):
630 self.rapi.AddResponse("[]")
631 self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
632 self.assertHandler(rlib2.R_2_instances_name_tags)
633 self.assertItems(["fooinstance"])
635 def testAddInstanceTags(self):
636 self.rapi.AddResponse("1234")
637 self.assertEqual(1234,
638 self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
639 self.assertHandler(rlib2.R_2_instances_name_tags)
640 self.assertItems(["fooinstance"])
642 self.assertQuery("tag", ["awesome"])
644 def testDeleteInstanceTags(self):
645 self.rapi.AddResponse("25826")
646 self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
648 self.assertHandler(rlib2.R_2_instances_name_tags)
649 self.assertItems(["foo"])
651 self.assertQuery("tag", ["awesome"])
653 def testRebootInstance(self):
654 self.rapi.AddResponse("6146")
655 job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
656 ignore_secondaries=True, dry_run=True)
657 self.assertEqual(6146, job_id)
658 self.assertHandler(rlib2.R_2_instances_name_reboot)
659 self.assertItems(["i-bar"])
661 self.assertQuery("type", ["hard"])
662 self.assertQuery("ignore_secondaries", ["1"])
664 def testShutdownInstance(self):
665 self.rapi.AddResponse("1487")
666 self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
668 self.assertHandler(rlib2.R_2_instances_name_shutdown)
669 self.assertItems(["foo-instance"])
672 def testStartupInstance(self):
673 self.rapi.AddResponse("27149")
674 self.assertEqual(27149, self.client.StartupInstance("bar-instance",
676 self.assertHandler(rlib2.R_2_instances_name_startup)
677 self.assertItems(["bar-instance"])
680 def testReinstallInstance(self):
681 self.rapi.AddResponse(serializer.DumpJson([]))
682 self.rapi.AddResponse("19119")
683 self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
686 self.assertHandler(rlib2.R_2_instances_name_reinstall)
687 self.assertItems(["baz-instance"])
688 self.assertQuery("os", ["DOS"])
689 self.assertQuery("nostartup", ["1"])
690 self.assertEqual(self.rapi.CountPending(), 0)
692 def testReinstallInstanceNew(self):
693 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
694 self.rapi.AddResponse("25689")
695 self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
698 self.assertHandler(rlib2.R_2_instances_name_reinstall)
699 self.assertItems(["moo-instance"])
700 data = serializer.LoadJson(self.rapi.GetLastRequestData())
701 self.assertEqual(len(data), 2)
702 self.assertEqual(data["os"], "Debian")
703 self.assertEqual(data["start"], False)
704 self.assertEqual(self.rapi.CountPending(), 0)
706 def testReinstallInstanceWithOsparams1(self):
707 self.rapi.AddResponse(serializer.DumpJson([]))
708 self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
709 "doo-instance", osparams={"x": "y"})
710 self.assertEqual(self.rapi.CountPending(), 0)
712 def testReinstallInstanceWithOsparams2(self):
717 self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
718 self.rapi.AddResponse("1717")
719 self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
721 self.assertHandler(rlib2.R_2_instances_name_reinstall)
722 self.assertItems(["zoo-instance"])
723 data = serializer.LoadJson(self.rapi.GetLastRequestData())
724 self.assertEqual(len(data), 2)
725 self.assertEqual(data["osparams"], osparams)
726 self.assertEqual(data["start"], True)
727 self.assertEqual(self.rapi.CountPending(), 0)
729 def testReplaceInstanceDisks(self):
730 self.rapi.AddResponse("999")
731 job_id = self.client.ReplaceInstanceDisks("instance-name",
732 disks=[0, 1], dry_run=True, iallocator="hail")
733 self.assertEqual(999, job_id)
734 self.assertHandler(rlib2.R_2_instances_name_replace_disks)
735 self.assertItems(["instance-name"])
736 self.assertQuery("disks", ["0,1"])
737 self.assertQuery("mode", ["replace_auto"])
738 self.assertQuery("iallocator", ["hail"])
741 self.rapi.AddResponse("1000")
742 job_id = self.client.ReplaceInstanceDisks("instance-bar",
743 disks=[1], mode="replace_on_secondary", remote_node="foo-node",
745 self.assertEqual(1000, job_id)
746 self.assertItems(["instance-bar"])
747 self.assertQuery("disks", ["1"])
748 self.assertQuery("remote_node", ["foo-node"])
751 self.rapi.AddResponse("5175")
752 self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
753 self.assertItems(["instance-moo"])
754 self.assertQuery("disks", None)
756 def testPrepareExport(self):
757 self.rapi.AddResponse("8326")
758 self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
759 self.assertHandler(rlib2.R_2_instances_name_prepare_export)
760 self.assertItems(["inst1"])
761 self.assertQuery("mode", ["local"])
763 def testExportInstance(self):
764 self.rapi.AddResponse("19695")
765 job_id = self.client.ExportInstance("inst2", "local", "nodeX",
767 self.assertEqual(job_id, 19695)
768 self.assertHandler(rlib2.R_2_instances_name_export)
769 self.assertItems(["inst2"])
771 data = serializer.LoadJson(self.rapi.GetLastRequestData())
772 self.assertEqual(data["mode"], "local")
773 self.assertEqual(data["destination"], "nodeX")
774 self.assertEqual(data["shutdown"], True)
776 def testMigrateInstanceDefaults(self):
777 self.rapi.AddResponse("24873")
778 job_id = self.client.MigrateInstance("inst91")
779 self.assertEqual(job_id, 24873)
780 self.assertHandler(rlib2.R_2_instances_name_migrate)
781 self.assertItems(["inst91"])
783 data = serializer.LoadJson(self.rapi.GetLastRequestData())
784 self.assertFalse(data)
786 def testMigrateInstance(self):
787 for mode in constants.HT_MIGRATION_MODES:
788 for cleanup in [False, True]:
789 self.rapi.AddResponse("31910")
790 job_id = self.client.MigrateInstance("inst289", mode=mode,
792 self.assertEqual(job_id, 31910)
793 self.assertHandler(rlib2.R_2_instances_name_migrate)
794 self.assertItems(["inst289"])
796 data = serializer.LoadJson(self.rapi.GetLastRequestData())
797 self.assertEqual(len(data), 2)
798 self.assertEqual(data["mode"], mode)
799 self.assertEqual(data["cleanup"], cleanup)
801 def testRenameInstanceDefaults(self):
802 new_name = "newnametha7euqu"
803 self.rapi.AddResponse("8791")
804 job_id = self.client.RenameInstance("inst18821", new_name)
805 self.assertEqual(job_id, 8791)
806 self.assertHandler(rlib2.R_2_instances_name_rename)
807 self.assertItems(["inst18821"])
809 data = serializer.LoadJson(self.rapi.GetLastRequestData())
810 self.assertEqualValues(data, {"new_name": new_name, })
812 def testRenameInstance(self):
813 new_name = "new-name-yiux1iin"
814 for ip_check in [False, True]:
815 for name_check in [False, True]:
816 self.rapi.AddResponse("24776")
817 job_id = self.client.RenameInstance("inst20967", new_name,
819 name_check=name_check)
820 self.assertEqual(job_id, 24776)
821 self.assertHandler(rlib2.R_2_instances_name_rename)
822 self.assertItems(["inst20967"])
824 data = serializer.LoadJson(self.rapi.GetLastRequestData())
825 self.assertEqual(len(data), 3)
826 self.assertEqual(data["new_name"], new_name)
827 self.assertEqual(data["ip_check"], ip_check)
828 self.assertEqual(data["name_check"], name_check)
830 def testGetJobs(self):
831 self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
832 ' { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
833 self.assertEqual([123, 124], self.client.GetJobs())
834 self.assertHandler(rlib2.R_2_jobs)
836 def testGetJobStatus(self):
837 self.rapi.AddResponse("{\"foo\": \"bar\"}")
838 self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
839 self.assertHandler(rlib2.R_2_jobs_id)
840 self.assertItems(["1234"])
842 def testWaitForJobChange(self):
843 fields = ["id", "summary"]
845 "job_info": [123, "something"],
849 self.rapi.AddResponse(serializer.DumpJson(expected))
850 result = self.client.WaitForJobChange(123, fields, [], -1)
851 self.assertEqualValues(expected, result)
852 self.assertHandler(rlib2.R_2_jobs_id_wait)
853 self.assertItems(["123"])
855 def testCancelJob(self):
856 self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
857 self.assertEqual([True, "Job 123 will be canceled"],
858 self.client.CancelJob(999, dry_run=True))
859 self.assertHandler(rlib2.R_2_jobs_id)
860 self.assertItems(["999"])
863 def testGetNodes(self):
864 self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
865 " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
866 self.assertEqual(["node1", "node2"], self.client.GetNodes())
867 self.assertHandler(rlib2.R_2_nodes)
869 self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
870 " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
871 self.assertEqual([{"id": "node1", "uri": "uri1"},
872 {"id": "node2", "uri": "uri2"}],
873 self.client.GetNodes(bulk=True))
874 self.assertHandler(rlib2.R_2_nodes)
877 def testGetNode(self):
878 self.rapi.AddResponse("{}")
879 self.assertEqual({}, self.client.GetNode("node-foo"))
880 self.assertHandler(rlib2.R_2_nodes_name)
881 self.assertItems(["node-foo"])
883 def testEvacuateNode(self):
884 self.rapi.AddResponse("9876")
885 job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
886 self.assertEqual(9876, job_id)
887 self.assertHandler(rlib2.R_2_nodes_name_evacuate)
888 self.assertItems(["node-1"])
889 self.assertQuery("remote_node", ["node-2"])
891 self.rapi.AddResponse("8888")
892 job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
893 self.assertEqual(8888, job_id)
894 self.assertItems(["node-3"])
895 self.assertQuery("iallocator", ["hail"])
898 self.assertRaises(client.GanetiApiError,
899 self.client.EvacuateNode,
900 "node-4", iallocator="hail", remote_node="node-5")
902 def testMigrateNode(self):
903 self.rapi.AddResponse("1111")
904 self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
905 self.assertHandler(rlib2.R_2_nodes_name_migrate)
906 self.assertItems(["node-a"])
907 self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
910 self.rapi.AddResponse("1112")
911 self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
913 self.assertHandler(rlib2.R_2_nodes_name_migrate)
914 self.assertItems(["node-a"])
915 self.assertQuery("mode", ["live"])
918 def testGetNodeRole(self):
919 self.rapi.AddResponse("\"master\"")
920 self.assertEqual("master", self.client.GetNodeRole("node-a"))
921 self.assertHandler(rlib2.R_2_nodes_name_role)
922 self.assertItems(["node-a"])
924 def testSetNodeRole(self):
925 self.rapi.AddResponse("789")
926 self.assertEqual(789,
927 self.client.SetNodeRole("node-foo", "master-candidate", force=True))
928 self.assertHandler(rlib2.R_2_nodes_name_role)
929 self.assertItems(["node-foo"])
930 self.assertQuery("force", ["1"])
931 self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
933 def testGetNodeStorageUnits(self):
934 self.rapi.AddResponse("42")
936 self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
937 self.assertHandler(rlib2.R_2_nodes_name_storage)
938 self.assertItems(["node-x"])
939 self.assertQuery("storage_type", ["lvm-pv"])
940 self.assertQuery("output_fields", ["fields"])
942 def testModifyNodeStorageUnits(self):
943 self.rapi.AddResponse("14")
945 self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
946 self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
947 self.assertItems(["node-z"])
948 self.assertQuery("storage_type", ["lvm-pv"])
949 self.assertQuery("name", ["hda"])
950 self.assertQuery("allocatable", None)
952 for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
953 self.rapi.AddResponse("7205")
954 job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
955 allocatable=allocatable)
956 self.assertEqual(7205, job_id)
957 self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
958 self.assertItems(["node-z"])
959 self.assertQuery("storage_type", ["lvm-pv"])
960 self.assertQuery("name", ["hda"])
961 self.assertQuery("allocatable", [query_allocatable])
963 def testRepairNodeStorageUnits(self):
964 self.rapi.AddResponse("99")
965 self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
967 self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
968 self.assertItems(["node-z"])
969 self.assertQuery("storage_type", ["lvm-pv"])
970 self.assertQuery("name", ["hda"])
972 def testGetNodeTags(self):
973 self.rapi.AddResponse("[\"fry\", \"bender\"]")
974 self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
975 self.assertHandler(rlib2.R_2_nodes_name_tags)
976 self.assertItems(["node-k"])
978 def testAddNodeTags(self):
979 self.rapi.AddResponse("1234")
980 self.assertEqual(1234,
981 self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
982 self.assertHandler(rlib2.R_2_nodes_name_tags)
983 self.assertItems(["node-v"])
985 self.assertQuery("tag", ["awesome"])
987 def testDeleteNodeTags(self):
988 self.rapi.AddResponse("16861")
989 self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
991 self.assertHandler(rlib2.R_2_nodes_name_tags)
992 self.assertItems(["node-w"])
994 self.assertQuery("tag", ["awesome"])
996 def testGetGroups(self):
997 groups = [{"name": "group1",
998 "uri": "/2/groups/group1",
1001 "uri": "/2/groups/group2",
1004 self.rapi.AddResponse(serializer.DumpJson(groups))
1005 self.assertEqual(["group1", "group2"], self.client.GetGroups())
1006 self.assertHandler(rlib2.R_2_groups)
1008 def testGetGroupsBulk(self):
1009 groups = [{"name": "group1",
1010 "uri": "/2/groups/group1",
1012 "node_list": ["gnt1.test",
1017 "uri": "/2/groups/group2",
1019 "node_list": ["gnt3.test",
1023 self.rapi.AddResponse(serializer.DumpJson(groups))
1025 self.assertEqual(groups, self.client.GetGroups(bulk=True))
1026 self.assertHandler(rlib2.R_2_groups)
1029 def testGetGroup(self):
1030 group = {"ctime": None,
1033 self.rapi.AddResponse(serializer.DumpJson(group))
1034 self.assertEqual({"ctime": None, "name": "default"},
1035 self.client.GetGroup("default"))
1036 self.assertHandler(rlib2.R_2_groups_name)
1037 self.assertItems(["default"])
1039 def testCreateGroup(self):
1040 self.rapi.AddResponse("12345")
1041 job_id = self.client.CreateGroup("newgroup", dry_run=True)
1042 self.assertEqual(job_id, 12345)
1043 self.assertHandler(rlib2.R_2_groups)
1046 def testDeleteGroup(self):
1047 self.rapi.AddResponse("12346")
1048 job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1049 self.assertEqual(job_id, 12346)
1050 self.assertHandler(rlib2.R_2_groups_name)
1053 def testRenameGroup(self):
1054 self.rapi.AddResponse("12347")
1055 job_id = self.client.RenameGroup("oldname", "newname")
1056 self.assertEqual(job_id, 12347)
1057 self.assertHandler(rlib2.R_2_groups_name_rename)
1059 def testModifyGroup(self):
1060 self.rapi.AddResponse("12348")
1061 job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1062 self.assertEqual(job_id, 12348)
1063 self.assertHandler(rlib2.R_2_groups_name_modify)
1065 def testAssignGroupNodes(self):
1066 self.rapi.AddResponse("12349")
1067 job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1068 force=True, dry_run=True)
1069 self.assertEqual(job_id, 12349)
1070 self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1072 self.assertUseForce()
1074 def testModifyInstance(self):
1075 self.rapi.AddResponse("23681")
1076 job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1077 self.assertEqual(job_id, 23681)
1078 self.assertItems(["inst7210"])
1079 self.assertHandler(rlib2.R_2_instances_name_modify)
1080 self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1081 { "os_name": "linux", })
1083 def testModifyCluster(self):
1084 for mnh in [None, False, True]:
1085 self.rapi.AddResponse("14470")
1086 self.assertEqual(14470,
1087 self.client.ModifyCluster(maintain_node_health=mnh))
1088 self.assertHandler(rlib2.R_2_cluster_modify)
1089 self.assertItems([])
1090 data = serializer.LoadJson(self.rapi.GetLastRequestData())
1091 self.assertEqual(len(data), 1)
1092 self.assertEqual(data["maintain_node_health"], mnh)
1093 self.assertEqual(self.rapi.CountPending(), 0)
1095 def testRedistributeConfig(self):
1096 self.rapi.AddResponse("3364")
1097 job_id = self.client.RedistributeConfig()
1098 self.assertEqual(job_id, 3364)
1099 self.assertItems([])
1100 self.assertHandler(rlib2.R_2_redist_config)
1102 def testActivateInstanceDisks(self):
1103 self.rapi.AddResponse("23547")
1104 job_id = self.client.ActivateInstanceDisks("inst28204")
1105 self.assertEqual(job_id, 23547)
1106 self.assertItems(["inst28204"])
1107 self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1108 self.assertFalse(self.rapi.GetLastHandler().queryargs)
1110 def testActivateInstanceDisksIgnoreSize(self):
1111 self.rapi.AddResponse("11044")
1112 job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1113 self.assertEqual(job_id, 11044)
1114 self.assertItems(["inst28204"])
1115 self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1116 self.assertQuery("ignore_size", ["1"])
1118 def testDeactivateInstanceDisks(self):
1119 self.rapi.AddResponse("14591")
1120 job_id = self.client.DeactivateInstanceDisks("inst28234")
1121 self.assertEqual(job_id, 14591)
1122 self.assertItems(["inst28234"])
1123 self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1124 self.assertFalse(self.rapi.GetLastHandler().queryargs)
1126 def testGetInstanceConsole(self):
1127 self.rapi.AddResponse("26876")
1128 job_id = self.client.GetInstanceConsole("inst21491")
1129 self.assertEqual(job_id, 26876)
1130 self.assertItems(["inst21491"])
1131 self.assertHandler(rlib2.R_2_instances_name_console)
1132 self.assertFalse(self.rapi.GetLastHandler().queryargs)
1133 self.assertFalse(self.rapi.GetLastRequestData())
1135 def testGrowInstanceDisk(self):
1136 for idx, wait_for_sync in enumerate([None, False, True]):
1137 amount = 128 + (512 * idx)
1138 self.assertEqual(self.rapi.CountPending(), 0)
1139 self.rapi.AddResponse("30783")
1140 self.assertEqual(30783,
1141 self.client.GrowInstanceDisk("eze8ch", idx, amount,
1142 wait_for_sync=wait_for_sync))
1143 self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1144 self.assertItems(["eze8ch", str(idx)])
1145 data = serializer.LoadJson(self.rapi.GetLastRequestData())
1146 if wait_for_sync is None:
1147 self.assertEqual(len(data), 1)
1148 self.assert_("wait_for_sync" not in data)
1150 self.assertEqual(len(data), 2)
1151 self.assertEqual(data["wait_for_sync"], wait_for_sync)
1152 self.assertEqual(data["amount"], amount)
1153 self.assertEqual(self.rapi.CountPending(), 0)
1156 class RapiTestRunner(unittest.TextTestRunner):
1157 def run(self, *args):
1158 global _used_handlers
1159 assert _used_handlers is None
1161 _used_handlers = set()
1164 result = unittest.TextTestRunner.run(self, *args)
1166 diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1169 raise AssertionError("The following RAPI resources were not used by the"
1170 " RAPI client: %r" % utils.CommaJoin(diff))
1172 # Reset global variable
1173 _used_handlers = None
1178 if __name__ == '__main__':
1179 client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)