KEY_ARGS = "args"
KEY_SUCCESS = "success"
KEY_RESULT = "result"
+KEY_VERSION = "version"
REQ_SUBMIT_JOB = "SubmitJob"
REQ_SUBMIT_MANY_JOBS = "SubmitManyJobs"
method = request.get(KEY_METHOD, None) # pylint: disable-msg=E1103
args = request.get(KEY_ARGS, None) # pylint: disable-msg=E1103
+ version = request.get(KEY_VERSION, None)
if method is None or args is None:
logging.error("LUXI request missing method or arguments: %r", msg)
raise ProtocolError(("Invalid LUXI request (no method or arguments"
" in request): %r") % msg)
- return (method, args)
+ return (method, args, version)
def ParseResponse(msg):
KEY_RESULT in data):
raise ProtocolError("Invalid response from server: %r" % data)
- return (data[KEY_SUCCESS], data[KEY_RESULT])
+ return (data[KEY_SUCCESS], data[KEY_RESULT], data.get(KEY_VERSION, None))
-def FormatResponse(success, result):
+def FormatResponse(success, result, version=None):
"""Formats a LUXI response message.
"""
KEY_RESULT: result,
}
+ if version is not None:
+ response[KEY_VERSION] = version
+
logging.debug("LUXI response: %s", response)
return serializer.DumpJson(response)
-def FormatRequest(method, args):
+def FormatRequest(method, args, version=None):
"""Formats a LUXI request message.
"""
KEY_ARGS: args,
}
+ if version is not None:
+ request[KEY_VERSION] = version
+
# Serialize the request
return serializer.DumpJson(request, indent=False)
-def CallLuxiMethod(transport_cb, method, args):
+def CallLuxiMethod(transport_cb, method, args, version=None):
"""Send a LUXI request via a transport and return the response.
"""
assert callable(transport_cb)
- request_msg = FormatRequest(method, args)
+ request_msg = FormatRequest(method, args, version=version)
# Send request and wait for response
response_msg = transport_cb(request_msg)
- (success, result) = ParseResponse(response_msg)
+ (success, result, resp_version) = ParseResponse(response_msg)
+
+ # Verify version if there was one in the response
+ if resp_version is not None and resp_version != version:
+ raise errors.LuxiError("LUXI version mismatch, client %s, response %s" %
+ (version, resp_version))
if success:
return result
"""Send a generic request and return the response.
"""
- return CallLuxiMethod(self._SendMethodCall, method, args)
+ return CallLuxiMethod(self._SendMethodCall, method, args,
+ version=constants.LUXI_VERSION)
def SetQueueDrainFlag(self, drain_flag):
return self.CallMethod(REQ_QUEUE_SET_DRAIN_FLAG, drain_flag)
import unittest
+from ganeti import constants
+from ganeti import errors
from ganeti import luxi
from ganeti import serializer
})
self.assertEqualValues(luxi.ParseRequest(msg),
- ("foo", ["bar", "baz", 123]))
+ ("foo", ["bar", "baz", 123], None))
self.assertRaises(luxi.ProtocolError, luxi.ParseRequest,
"this\"is {invalid, ]json data")
self.assertRaises(luxi.ProtocolError, luxi.ParseRequest,
serializer.DumpJson({ luxi.KEY_ARGS: [], }))
+ # No method or arguments
+ self.assertRaises(luxi.ProtocolError, luxi.ParseRequest,
+ serializer.DumpJson({ luxi.KEY_VERSION: 1, }))
+
+ def testParseRequestWithVersion(self):
+ msg = serializer.DumpJson({
+ luxi.KEY_METHOD: "version",
+ luxi.KEY_ARGS: (["some"], "args", 0, "here"),
+ luxi.KEY_VERSION: 20100101,
+ })
+
+ self.assertEqualValues(luxi.ParseRequest(msg),
+ ("version", [["some"], "args", 0, "here"], 20100101))
+
def testParseResponse(self):
msg = serializer.DumpJson({
luxi.KEY_SUCCESS: True,
luxi.KEY_RESULT: None,
})
- self.assertEqual(luxi.ParseResponse(msg), (True, None))
+ self.assertEqual(luxi.ParseResponse(msg), (True, None, None))
self.assertRaises(luxi.ProtocolError, luxi.ParseResponse,
"this\"is {invalid, ]json data")
self.assertRaises(luxi.ProtocolError, luxi.ParseResponse,
serializer.DumpJson({ luxi.KEY_SUCCESS: True, }))
+ # No result or success
+ self.assertRaises(luxi.ProtocolError, luxi.ParseResponse,
+ serializer.DumpJson({ luxi.KEY_VERSION: 123, }))
+
+ def testParseResponseWithVersion(self):
+ msg = serializer.DumpJson({
+ luxi.KEY_SUCCESS: True,
+ luxi.KEY_RESULT: "Hello World",
+ luxi.KEY_VERSION: 19991234,
+ })
+
+ self.assertEqual(luxi.ParseResponse(msg), (True, "Hello World", 19991234))
+
def testFormatResponse(self):
for success, result in [(False, "error"), (True, "abc"),
(True, { "a": 123, "b": None, })]:
msgdata = serializer.LoadJson(msg)
self.assert_(luxi.KEY_SUCCESS in msgdata)
self.assert_(luxi.KEY_RESULT in msgdata)
+ self.assert_(luxi.KEY_VERSION not in msgdata)
+ self.assertEqualValues(msgdata,
+ { luxi.KEY_SUCCESS: success,
+ luxi.KEY_RESULT: result,
+ })
+
+ def testFormatResponseWithVersion(self):
+ for success, result, version in [(False, "error", 123), (True, "abc", 999),
+ (True, { "a": 123, "b": None, }, 2010)]:
+ msg = luxi.FormatResponse(success, result, version=version)
+ msgdata = serializer.LoadJson(msg)
+ self.assert_(luxi.KEY_SUCCESS in msgdata)
+ self.assert_(luxi.KEY_RESULT in msgdata)
+ self.assert_(luxi.KEY_VERSION in msgdata)
self.assertEqualValues(msgdata,
{ luxi.KEY_SUCCESS: success,
luxi.KEY_RESULT: result,
+ luxi.KEY_VERSION: version,
})
def testFormatRequest(self):
msgdata = serializer.LoadJson(msg)
self.assert_(luxi.KEY_METHOD in msgdata)
self.assert_(luxi.KEY_ARGS in msgdata)
+ self.assert_(luxi.KEY_VERSION not in msgdata)
+ self.assertEqualValues(msgdata,
+ { luxi.KEY_METHOD: method,
+ luxi.KEY_ARGS: args,
+ })
+
+ def testFormatRequestWithVersion(self):
+ for method, args, version in [("fn1", [], 123), ("fn2", [1, 2, 3], 999)]:
+ msg = luxi.FormatRequest(method, args, version=version)
+ msgdata = serializer.LoadJson(msg)
+ self.assert_(luxi.KEY_METHOD in msgdata)
+ self.assert_(luxi.KEY_ARGS in msgdata)
+ self.assert_(luxi.KEY_VERSION in msgdata)
self.assertEqualValues(msgdata,
{ luxi.KEY_METHOD: method,
luxi.KEY_ARGS: args,
+ luxi.KEY_VERSION: version,
})
+class TestCallLuxiMethod(unittest.TestCase):
+ MY_LUXI_VERSION = 1234
+ assert constants.LUXI_VERSION != MY_LUXI_VERSION
+
+ def testSuccessNoVersion(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fn1")
+ self.assertEqual(args, "Hello World")
+ return luxi.FormatResponse(True, "x")
+
+ result = luxi.CallLuxiMethod(_Cb, "fn1", "Hello World")
+
+ def testServerVersionOnly(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fn1")
+ self.assertEqual(args, "Hello World")
+ return luxi.FormatResponse(True, "x", version=self.MY_LUXI_VERSION)
+
+ self.assertRaises(errors.LuxiError, luxi.CallLuxiMethod,
+ _Cb, "fn1", "Hello World")
+
+ def testWithVersion(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fn99")
+ self.assertEqual(args, "xyz")
+ return luxi.FormatResponse(True, "y", version=self.MY_LUXI_VERSION)
+
+ self.assertEqual("y", luxi.CallLuxiMethod(_Cb, "fn99", "xyz",
+ version=self.MY_LUXI_VERSION))
+
+ def testVersionMismatch(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fn5")
+ self.assertEqual(args, "xyz")
+ return luxi.FormatResponse(True, "F", version=self.MY_LUXI_VERSION * 2)
+
+ self.assertRaises(errors.LuxiError, luxi.CallLuxiMethod,
+ _Cb, "fn5", "xyz", version=self.MY_LUXI_VERSION)
+
+ def testError(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fnErr")
+ self.assertEqual(args, [])
+ err = errors.OpPrereqError("Test")
+ return luxi.FormatResponse(False, errors.EncodeException(err))
+
+ self.assertRaises(errors.OpPrereqError, luxi.CallLuxiMethod,
+ _Cb, "fnErr", [])
+
+ def testErrorWithVersionMismatch(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fnErr")
+ self.assertEqual(args, [])
+ err = errors.OpPrereqError("TestVer")
+ return luxi.FormatResponse(False, errors.EncodeException(err),
+ version=self.MY_LUXI_VERSION * 2)
+
+ self.assertRaises(errors.LuxiError, luxi.CallLuxiMethod,
+ _Cb, "fnErr", [],
+ version=self.MY_LUXI_VERSION)
+
+ def testErrorWithVersion(self):
+ def _Cb(msg):
+ (method, args, version) = luxi.ParseRequest(msg)
+ self.assertEqual(method, "fn9")
+ self.assertEqual(args, [])
+ err = errors.OpPrereqError("TestVer")
+ return luxi.FormatResponse(False, errors.EncodeException(err),
+ version=self.MY_LUXI_VERSION)
+
+ self.assertRaises(errors.OpPrereqError, luxi.CallLuxiMethod,
+ _Cb, "fn9", [],
+ version=self.MY_LUXI_VERSION)
+
+
if __name__ == "__main__":
testutils.GanetiTestProgram()