"""Script for unittesting the RAPI client module"""
-try:
- import httplib2
- BaseHttp = httplib2.Http
- from ganeti.rapi import client
-except ImportError:
- httplib2 = None
- BaseHttp = object
-
import re
import unittest
import warnings
from ganeti import http
+from ganeti import serializer
from ganeti.rapi import connector
from ganeti.rapi import rlib2
+from ganeti.rapi import client
import testutils
return None
-class HttpResponseMock(dict):
- """Dumb mock of httplib2.Response.
+class HttpResponseMock:
+ """Dumb mock of httplib.HTTPResponse.
"""
- def __init__(self, status):
- self.status = status
- self['status'] = status
+ def __init__(self, code, data):
+ self.code = code
+ self._data = data
+
+ def read(self):
+ return self._data
-class HttpMock(BaseHttp):
- """Mock for httplib.Http.
+class OpenerDirectorMock:
+ """Mock for urllib.OpenerDirector.
"""
def __init__(self, rapi):
self._rapi = rapi
- self._last_request = None
+ self.last_request = None
- last_request_url = property(lambda self: self._last_request[0])
- last_request_method = property(lambda self: self._last_request[1])
- last_request_body = property(lambda self: self._last_request[2])
+ def open(self, req):
+ self.last_request = req
- def request(self, url, method, body, headers):
- self._last_request = (url, method, body)
- code, resp_body = self._rapi.FetchResponse(_GetPathFromUri(url), method)
- return HttpResponseMock(code), resp_body
+ path = _GetPathFromUri(req.get_full_url())
+ code, resp_body = self._rapi.FetchResponse(path, req.get_method())
+ return HttpResponseMock(code, resp_body)
class RapiMock(object):
-
def __init__(self):
self._mapper = connector.Mapper()
self._responses = []
self._last_handler = None
- def AddResponse(self, response):
- self._responses.insert(0, response)
-
- def PopResponse(self):
- if len(self._responses) > 0:
- return self._responses.pop()
- else:
- return None
+ def AddResponse(self, response, code=200):
+ self._responses.insert(0, (code, response))
def GetLastHandler(self):
return self._last_handler
def FetchResponse(self, path, method):
- code = 200
- response = None
-
try:
HandlerClass, items, args = self._mapper.getController(path)
self._last_handler = HandlerClass(items, args, None)
if not hasattr(self._last_handler, method.upper()):
- code = 400
- response = "Bad request"
+ raise http.HttpNotImplemented(message="Method not implemented")
+
except http.HttpException, ex:
code = ex.code
response = ex.message
+ else:
+ if not self._responses:
+ raise Exception("No responses")
- if not response:
- response = self.PopResponse()
+ (code, response) = self._responses.pop()
return code, response
class RapiMockTest(unittest.TestCase):
-
def test(self):
rapi = RapiMock()
path = "/version"
self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET"))
- self.assertEqual((400, "Bad request"),
+ self.assertEqual((501, "Method not implemented"),
rapi.FetchResponse("/version", "POST"))
rapi.AddResponse("2")
code, response = rapi.FetchResponse("/version", "GET")
self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
-class GanetiRapiClientTests(unittest.TestCase):
- """Tests for remote API client.
-
- """
-
+class GanetiRapiClientTests(testutils.GanetiTestCase):
def setUp(self):
- # Monkey-patch a fake VerifyCertificate function
- self._verify_certificate = client._VerifyCertificate
- client._VerifyCertificate = lambda x, y, z: True
+ testutils.GanetiTestCase.setUp(self)
self.rapi = RapiMock()
- self.http = HttpMock(self.rapi)
+ self.http = OpenerDirectorMock(self.rapi)
self.client = client.GanetiRapiClient('master.foo.com')
self.client._http = self.http
# Hard-code the version for easier testing.
self.client._version = 2
- def tearDown(self):
- # Un-do the monkey-patch
- client._VerifyCertificate = self._verify_certificate
-
def assertHandler(self, handler_cls):
self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
def assertDryRun(self):
self.assertTrue(self.rapi.GetLastHandler().dryRun())
+ def testEncodeQuery(self):
+ query = [
+ ("a", None),
+ ("b", 1),
+ ("c", 2),
+ ("d", "Foo"),
+ ("e", True),
+ ]
+
+ expected = [
+ ("a", ""),
+ ("b", 1),
+ ("c", 2),
+ ("d", "Foo"),
+ ("e", 1),
+ ]
+
+ self.assertEqualValues(self.client._EncodeQuery(query),
+ expected)
+
+ # invalid types
+ for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
+ self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
+
+ def testHttpError(self):
+ self.rapi.AddResponse(None, code=404)
+ try:
+ self.client.GetJobStatus(15140)
+ except client.GanetiApiError, err:
+ self.assertEqual(err.code, 404)
+ else:
+ self.fail("Didn't raise exception")
+
def testGetVersion(self):
self.client._version = None
self.rapi.AddResponse("2")
self.assertEqual(2, self.client.GetVersion())
self.assertHandler(rlib2.R_version)
+ def testGetFeatures(self):
+ for features in [[], ["foo", "bar", "baz"]]:
+ self.rapi.AddResponse(serializer.DumpJson(features))
+ self.assertEqual(features, self.client.GetFeatures())
+ self.assertHandler(rlib2.R_2_features)
+
def testGetOperatingSystems(self):
self.rapi.AddResponse("[\"beos\"]")
self.assertEqual(["beos"], self.client.GetOperatingSystems())
self.assertQuery("tag", ["awesome"])
def testDeleteClusterTags(self):
- self.client.DeleteClusterTags(["awesome"], dry_run=True)
+ self.rapi.AddResponse("5107")
+ self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
+ dry_run=True))
self.assertHandler(rlib2.R_2_tags)
self.assertDryRun()
self.assertQuery("tag", ["awesome"])
self.assertHandler(rlib2.R_2_instances)
self.assertBulk()
- def testGetInstanceInfo(self):
+ def testGetInstance(self):
self.rapi.AddResponse("[]")
- self.assertEqual([], self.client.GetInstanceInfo("instance"))
+ self.assertEqual([], self.client.GetInstance("instance"))
self.assertHandler(rlib2.R_2_instances_name)
self.assertItems(["instance"])
+ def testGetInstanceInfo(self):
+ self.rapi.AddResponse("21291")
+ self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
+ self.assertHandler(rlib2.R_2_instances_name_info)
+ self.assertItems(["inst3"])
+ self.assertQuery("static", None)
+
+ self.rapi.AddResponse("3428")
+ self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
+ self.assertHandler(rlib2.R_2_instances_name_info)
+ self.assertItems(["inst31"])
+ self.assertQuery("static", ["0"])
+
+ self.rapi.AddResponse("15665")
+ self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
+ self.assertHandler(rlib2.R_2_instances_name_info)
+ self.assertItems(["inst32"])
+ self.assertQuery("static", ["1"])
+
+ def testCreateInstanceOldVersion(self):
+ self.rapi.AddResponse(serializer.DumpJson([]))
+ self.assertRaises(NotImplementedError, self.client.CreateInstance,
+ "create", "inst1.example.com", "plain", [], [],
+ dry_run=True)
+
def testCreateInstance(self):
- self.rapi.AddResponse("1234")
- self.assertEqual(1234, self.client.CreateInstance(dry_run=True))
+ self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
+ self.rapi.AddResponse("23030")
+ job_id = self.client.CreateInstance("create", "inst1.example.com",
+ "plain", [], [], dry_run=True)
+ self.assertEqual(job_id, 23030)
self.assertHandler(rlib2.R_2_instances)
self.assertDryRun()
+ data = serializer.LoadJson(self.http.last_request.data)
+
+ for field in ["dry_run", "beparams", "hvparams", "start"]:
+ self.assertFalse(field in data)
+
+ self.assertEqual(data["name"], "inst1.example.com")
+ self.assertEqual(data["disk_template"], "plain")
+
+ def testCreateInstance2(self):
+ self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
+ self.rapi.AddResponse("24740")
+ job_id = self.client.CreateInstance("import", "inst2.example.com",
+ "drbd8", [{"size": 100,}],
+ [{}, {"bridge": "br1", }],
+ dry_run=False, start=True,
+ pnode="node1", snode="node9",
+ ip_check=False)
+ self.assertEqual(job_id, 24740)
+ self.assertHandler(rlib2.R_2_instances)
+
+ data = serializer.LoadJson(self.http.last_request.data)
+ self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
+ self.assertEqual(data["name"], "inst2.example.com")
+ self.assertEqual(data["disk_template"], "drbd8")
+ self.assertEqual(data["start"], True)
+ self.assertEqual(data["ip_check"], False)
+ self.assertEqualValues(data["disks"], [{"size": 100,}])
+ self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
+
def testDeleteInstance(self):
self.rapi.AddResponse("1234")
self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
self.assertQuery("tag", ["awesome"])
def testDeleteInstanceTags(self):
- self.client.DeleteInstanceTags("foo", ["awesome"], dry_run=True)
+ self.rapi.AddResponse("25826")
+ self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
+ dry_run=True))
self.assertHandler(rlib2.R_2_instances_name_tags)
self.assertItems(["foo"])
self.assertDryRun()
self.assertQuery("tag", ["awesome"])
def testRebootInstance(self):
- self.client.RebootInstance("i-bar", reboot_type="hard",
- ignore_secondaries=True, dry_run=True)
+ self.rapi.AddResponse("6146")
+ job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
+ ignore_secondaries=True, dry_run=True)
+ self.assertEqual(6146, job_id)
self.assertHandler(rlib2.R_2_instances_name_reboot)
self.assertItems(["i-bar"])
self.assertDryRun()
self.assertQuery("type", ["hard"])
- self.assertQuery("ignore_secondaries", ["True"])
+ self.assertQuery("ignore_secondaries", ["1"])
def testShutdownInstance(self):
- self.client.ShutdownInstance("foo-instance", dry_run=True)
+ self.rapi.AddResponse("1487")
+ self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
+ dry_run=True))
self.assertHandler(rlib2.R_2_instances_name_shutdown)
self.assertItems(["foo-instance"])
self.assertDryRun()
def testStartupInstance(self):
- self.client.StartupInstance("bar-instance", dry_run=True)
+ self.rapi.AddResponse("27149")
+ self.assertEqual(27149, self.client.StartupInstance("bar-instance",
+ dry_run=True))
self.assertHandler(rlib2.R_2_instances_name_startup)
self.assertItems(["bar-instance"])
self.assertDryRun()
def testReinstallInstance(self):
- self.client.ReinstallInstance("baz-instance", "DOS", no_startup=True)
+ self.rapi.AddResponse("19119")
+ self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
+ no_startup=True))
self.assertHandler(rlib2.R_2_instances_name_reinstall)
self.assertItems(["baz-instance"])
self.assertQuery("os", ["DOS"])
def testReplaceInstanceDisks(self):
self.rapi.AddResponse("999")
job_id = self.client.ReplaceInstanceDisks("instance-name",
- ["hda", "hdc"], dry_run=True)
+ disks=[0, 1], dry_run=True, iallocator="hail")
self.assertEqual(999, job_id)
self.assertHandler(rlib2.R_2_instances_name_replace_disks)
self.assertItems(["instance-name"])
- self.assertQuery("disks", ["hda,hdc"])
+ self.assertQuery("disks", ["0,1"])
self.assertQuery("mode", ["replace_auto"])
self.assertQuery("iallocator", ["hail"])
self.assertDryRun()
- self.assertRaises(client.InvalidReplacementMode,
- self.client.ReplaceInstanceDisks,
- "instance_a", ["hda"], mode="invalid_mode")
- self.assertRaises(client.GanetiApiError,
- self.client.ReplaceInstanceDisks,
- "instance-foo", ["hda"], mode="replace_on_secondary")
-
self.rapi.AddResponse("1000")
job_id = self.client.ReplaceInstanceDisks("instance-bar",
- ["hda"], mode="replace_on_secondary", remote_node="foo-node",
+ disks=[1], mode="replace_on_secondary", remote_node="foo-node",
dry_run=True)
self.assertEqual(1000, job_id)
self.assertItems(["instance-bar"])
- self.assertQuery("disks", ["hda"])
+ self.assertQuery("disks", ["1"])
self.assertQuery("remote_node", ["foo-node"])
self.assertDryRun()
+ self.rapi.AddResponse("5175")
+ self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
+ self.assertItems(["instance-moo"])
+ self.assertQuery("disks", None)
+
+ def testPrepareExport(self):
+ self.rapi.AddResponse("8326")
+ self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
+ self.assertHandler(rlib2.R_2_instances_name_prepare_export)
+ self.assertItems(["inst1"])
+ self.assertQuery("mode", ["local"])
+
+ def testExportInstance(self):
+ self.rapi.AddResponse("19695")
+ job_id = self.client.ExportInstance("inst2", "local", "nodeX",
+ shutdown=True)
+ self.assertEqual(job_id, 19695)
+ self.assertHandler(rlib2.R_2_instances_name_export)
+ self.assertItems(["inst2"])
+
+ data = serializer.LoadJson(self.http.last_request.data)
+ self.assertEqual(data["mode"], "local")
+ self.assertEqual(data["destination"], "nodeX")
+ self.assertEqual(data["shutdown"], True)
+
def testGetJobs(self):
self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
' { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
self.assertHandler(rlib2.R_2_jobs_id)
self.assertItems(["1234"])
- def testDeleteJob(self):
- self.client.DeleteJob(999, dry_run=True)
+ def testWaitForJobChange(self):
+ fields = ["id", "summary"]
+ expected = {
+ "job_info": [123, "something"],
+ "log_entries": [],
+ }
+
+ self.rapi.AddResponse(serializer.DumpJson(expected))
+ result = self.client.WaitForJobChange(123, fields, [], -1)
+ self.assertEqualValues(expected, result)
+ self.assertHandler(rlib2.R_2_jobs_id_wait)
+ self.assertItems(["123"])
+
+ def testCancelJob(self):
+ self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
+ self.assertEqual([True, "Job 123 will be canceled"],
+ self.client.CancelJob(999, dry_run=True))
self.assertHandler(rlib2.R_2_jobs_id)
self.assertItems(["999"])
self.assertDryRun()
self.assertHandler(rlib2.R_2_nodes)
self.assertBulk()
- def testGetNodeInfo(self):
+ def testGetNode(self):
self.rapi.AddResponse("{}")
- self.assertEqual({}, self.client.GetNodeInfo("node-foo"))
+ self.assertEqual({}, self.client.GetNode("node-foo"))
self.assertHandler(rlib2.R_2_nodes_name)
self.assertItems(["node-foo"])
self.client.SetNodeRole("node-foo", "master-candidate", force=True))
self.assertHandler(rlib2.R_2_nodes_name_role)
self.assertItems(["node-foo"])
- self.assertQuery("force", ["True"])
- self.assertEqual("master-candidate", self.http.last_request_body)
-
- self.assertRaises(client.InvalidNodeRole,
- self.client.SetNodeRole, "node-bar", "fake-role")
+ self.assertQuery("force", ["1"])
+ self.assertEqual("\"master-candidate\"", self.http.last_request.data)
def testGetNodeStorageUnits(self):
self.rapi.AddResponse("42")
self.assertQuery("storage_type", ["lvm-pv"])
self.assertQuery("output_fields", ["fields"])
- self.assertRaises(client.InvalidStorageType,
- self.client.GetNodeStorageUnits,
- "node-y", "floppy-disk", "fields")
-
def testModifyNodeStorageUnits(self):
self.rapi.AddResponse("14")
self.assertEqual(14,
self.assertItems(["node-z"])
self.assertQuery("storage_type", ["lvm-pv"])
self.assertQuery("name", ["hda"])
-
- self.assertRaises(client.InvalidStorageType,
- self.client.ModifyNodeStorageUnits,
- "node-n", "floppy-disk", "hdc")
+ self.assertQuery("allocatable", None)
+
+ for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
+ self.rapi.AddResponse("7205")
+ job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
+ allocatable=allocatable)
+ self.assertEqual(7205, job_id)
+ self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
+ self.assertItems(["node-z"])
+ self.assertQuery("storage_type", ["lvm-pv"])
+ self.assertQuery("name", ["hda"])
+ self.assertQuery("allocatable", [query_allocatable])
+
+ def testRepairNodeStorageUnits(self):
+ self.rapi.AddResponse("99")
+ self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
+ "hda"))
+ self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
+ self.assertItems(["node-z"])
+ self.assertQuery("storage_type", ["lvm-pv"])
+ self.assertQuery("name", ["hda"])
def testGetNodeTags(self):
self.rapi.AddResponse("[\"fry\", \"bender\"]")
self.assertQuery("tag", ["awesome"])
def testDeleteNodeTags(self):
- self.client.DeleteNodeTags("node-w", ["awesome"], dry_run=True)
+ self.rapi.AddResponse("16861")
+ self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
+ dry_run=True))
self.assertHandler(rlib2.R_2_nodes_name_tags)
self.assertItems(["node-w"])
self.assertDryRun()
if __name__ == '__main__':
- if httplib2 is None:
- warnings.warn("These tests require the httplib2 library")
- else:
- testutils.GanetiTestProgram()
+ testutils.GanetiTestProgram()