Piotr Kliczewski has uploaded a new change for review. Change subject: [wip] bridge: usage of yaml schema ......................................................................
[wip] bridge: usage of yaml schema Change-Id: Id24a5e078fa92e4129d37a47593c7a167e78712e Signed-off-by: pkliczewski <piotr.kliczew...@gmail.com> --- M lib/api/schemaapi.py M tests/bridgeTests.py M vdsm/rpc/Bridge.py 3 files changed, 55 insertions(+), 113 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/19/53919/1 diff --git a/lib/api/schemaapi.py b/lib/api/schemaapi.py index 0040cce..0dd51fb 100644 --- a/lib/api/schemaapi.py +++ b/lib/api/schemaapi.py @@ -57,6 +57,20 @@ def get_params(self, class_name, method_name): return self.get_method(class_name, method_name).get('params', []) + def get_param_names(self, class_name, method_name): + return [param.get('name') for param in self.get_params(class_name, + method_name)] + + def get_default_param_names(self, class_name, method_name): + return [param.get('name') for param in self.get_params(class_name, + method_name) + if 'defaultvalue' in param] + + def get_default_param_values(self, class_name, method_name): + return [param.get('defaultvalue') + for param in self.get_params(class_name, method_name) + if 'defaultvalue' in param] + def get_ret_param(self, class_name, method_name): return self.get_method(class_name, method_name).get('return', {}) diff --git a/tests/bridgeTests.py b/tests/bridgeTests.py index df189fa..ce75f58 100644 --- a/tests/bridgeTests.py +++ b/tests/bridgeTests.py @@ -93,17 +93,17 @@ def _getApiInstance(self, className, argObj): - className = self._convertClassName(className) + className = self._convert_class_name(className) apiObj = getattr(getFakeAPI(), className) - ctorArgs = self._getArgs(argObj, apiObj.ctorArgs, []) + ctorArgs = self._get_args(argObj, apiObj.ctorArgs, [], []) return apiObj(*ctorArgs) class BridgeTests(TestCaseBase): - @MonkeyPatch(DynamicBridge, '_getApiInstance', _getApiInstance) + @MonkeyPatch(DynamicBridge, '_get_api_instance', _getApiInstance) def testMethodWithManyOptionalAttributes(self): bridge = DynamicBridge() @@ -114,7 +114,7 @@ self.assertEquals(bridge.dispatch('Host.fenceNode')(**params), {'power': 'on'}) - @MonkeyPatch(DynamicBridge, '_getApiInstance', _getApiInstance) + @MonkeyPatch(DynamicBridge, '_get_api_instance', _getApiInstance) def testMethodWithNoParams(self): bridge = DynamicBridge() @@ -123,7 +123,7 @@ ['My caps'], 'My capabilites') bridge.unregister_server_address() - @MonkeyPatch(DynamicBridge, '_getApiInstance', _getApiInstance) + @MonkeyPatch(DynamicBridge, '_get_api_instance', _getApiInstance) def testDetach(self): bridge = DynamicBridge() @@ -134,7 +134,7 @@ self.assertEqual(bridge.dispatch('StorageDomain.detach')(**params), None) - @MonkeyPatch(DynamicBridge, '_getApiInstance', _getApiInstance) + @MonkeyPatch(DynamicBridge, '_get_api_instance', _getApiInstance) def testHookError(self): bridge = DynamicBridge() diff --git a/vdsm/rpc/Bridge.py b/vdsm/rpc/Bridge.py index 1c42de4..6aae9ee 100644 --- a/vdsm/rpc/Bridge.py +++ b/vdsm/rpc/Bridge.py @@ -15,13 +15,13 @@ from functools import partial -import inspect import logging import threading import types import API import yajsonrpc +from api import schemaapi from api import vdsmapi from vdsm.netinfo.addresses import getDeviceByIP @@ -60,6 +60,7 @@ class DynamicBridge(object): def __init__(self): self.api = vdsmapi.get_api() + self._cache = schemaapi.SchemaCache() self._threadLocal = threading.local() self.log = logging.getLogger('DynamicBridge') @@ -69,17 +70,16 @@ def unregister_server_address(self): self._threadLocal.server = None - def _getArgs(self, argobj, arglist, defaultArgs): + def _get_args(self, argobj, arglist, defaultArgs, defaultValues): ret = () for arg in arglist: - if arg.startswith('*'): - name = self._symNameFilter(arg) - if name in argobj: - ret = ret + (argobj[name],) + if arg in defaultArgs: + if arg in argobj: + ret = ret + (argobj[arg],) else: - if len(defaultArgs) > 0: - ret = ret + (defaultArgs[0],) - defaultArgs = defaultArgs[1:] + if len(defaultValues) > 0: + ret = ret + (defaultValues[0],) + defaultValues = defaultValues[1:] elif arg in argobj: ret = ret + (argobj[arg],) return ret @@ -100,7 +100,7 @@ raise yajsonrpc.JsonRpcMethodNotFoundError(method) return partial(self._dynamicMethod, className, methodName) - def _convertClassName(self, name): + def _convert_class_name(self, name): """ The schema has a different name for the 'Global' namespace. Until API.py is fixed, we convert the schema name if we are looking up @@ -112,11 +112,11 @@ except KeyError: return name - def _getMethodArgs(self, className, methodName, argObj): + def _get_method_args(self, class_name, method_name, argObj): """ An internal API call currently looks like: - instance = API.<className>(<*ctor_args>) + instance = API.<class_name>(<*ctor_args>) intance.<method>(<*method_args>) Eventually we can remove this instancing but for now that's the way it @@ -124,111 +124,39 @@ them from here. For any given method, the method_args are obtained by chopping off the ctor_args from the beginning of argObj. """ - # Get the full argument list - sym = self.api['commands'][className][methodName] - allArgs = sym.get('data', {}).keys() + allArgs = self._cache.get_param_names(class_name, method_name) - className = self._convertClassName(className) - # Get the list of ctor_args + className = self._convert_class_name(class_name) if _glusterEnabled and className.startswith('Gluster'): ctorArgs = getattr(gapi, className).ctorArgs - defaultArgs = self._getDefaultArgs(gapi, className, methodName) else: ctorArgs = getattr(API, className).ctorArgs - defaultArgs = self._getDefaultArgs(API, className, methodName) + + defaultArgs = self._cache.get_default_param_names(class_name, + method_name) + defaultValues = self._cache.get_default_param_values(class_name, + method_name) # Determine the method arguments by subtraction methodArgs = [] for arg in allArgs: - name = self._symNameFilter(arg) - - if name not in ctorArgs: + if arg not in ctorArgs: methodArgs.append(arg) - return self._getArgs(argObj, methodArgs, defaultArgs) + return self._get_args(argObj, methodArgs, defaultArgs, defaultValues) - def _getDefaultArgs(self, api, className, methodName): - result = [] - for class_name, class_obj in inspect.getmembers(api, inspect.isclass): - if class_name == className: - for method_name, method_obj in inspect.getmembers( - class_obj, inspect.ismethod): - if method_name == methodName: - args = inspect.getargspec(method_obj).defaults - if args: - result = list(args) - return result - - def _getApiInstance(self, className, argObj): - className = self._convertClassName(className) + def _get_api_instance(self, className, argObj): + className = self._convert_class_name(className) if _glusterEnabled and className.startswith('Gluster'): apiObj = getattr(gapi, className) else: apiObj = getattr(API, className) - ctorArgs = self._getArgs(argObj, apiObj.ctorArgs, []) + ctorArgs = self._get_args(argObj, apiObj.ctorArgs, [], []) return apiObj(*ctorArgs) - def _symNameFilter(self, symName): - """ - The schema prefixes symbol names with '*' if they are optional. Strip - that annotation to get the correct symbol name. - """ - if symName.startswith('*'): - symName = symName[1:] - return symName - - # TODO: Add support for map types - def _typeFixup(self, symName, symTypeName, obj): - isList = False - if isinstance(symTypeName, list): - symTypeName = symTypeName[0] - isList = True - symName = self._symNameFilter(symName) - - try: - symbol = self.api['types'][symTypeName] - except KeyError: - return - - if isList: - itemList = obj - else: - itemList = [obj] - - for item in itemList: - if symTypeName in typefixups: - typefixups[symTypeName](item) - for (k, v) in symbol.get('data', {}).items(): - k = self._symNameFilter(k) - if k in item: - self._typeFixup(k, v, item[k]) - - def _fixupArgs(self, className, methodName, args): - argDef = self.api['commands'][className][methodName].get('data', {}) - argInfo = zip(argDef.items(), args) - for typeInfo, val in argInfo: - argName, argType = typeInfo - if isinstance(argType, list): - # check type of first element - argType = argType[0] - if argType not in self.api['types']: - continue - if val is None: - continue - self._typeFixup(argName, argType, val) - - def _fixupRet(self, className, methodName, result): - retType = self._getRetList(className, methodName) - if retType is not None: - self._typeFixup('return', retType, result) - return result - - def _getRetList(self, className, methodName): - return self.api['commands'][className][methodName].get('returns') - - def _nameArgs(self, args, kwargs, arglist): + def _name_args(self, args, kwargs, arglist): kwargs = kwargs.copy() for i, arg in enumerate(args): argName = arglist[i] @@ -236,16 +164,15 @@ return kwargs - def _getArgList(self, className, methodName): - sym = self.api['commands'][className][methodName] - return sym.get('data', {}).keys() - def _dynamicMethod(self, className, methodName, *args, **kwargs): - argobj = self._nameArgs(args, kwargs, - self._getArgList(className, methodName)) - api = self._getApiInstance(className, argobj) - methodArgs = self._getMethodArgs(className, methodName, argobj) - self._fixupArgs(className, methodName, methodArgs) + argobj = self._name_args(args, kwargs, + self._cache.get_param_names(className, + methodName)) + api = self._get_api_instance(className, argobj) + + methodArgs = self._get_method_args(className, methodName, argobj) + + # TODO verify types # Call the override function (if given). Otherwise, just call directly cmd = '%s_%s' % (className, methodName) @@ -284,7 +211,8 @@ if key is not 'status']) else: ret = self._getResult(result, retfield) - return self._fixupRet(className, methodName, ret) + # TODO verify response type + return ret def Host_fenceNode_Ret(ret): -- To view, visit https://gerrit.ovirt.org/53919 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id24a5e078fa92e4129d37a47593c7a167e78712e Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: Piotr Kliczewski <piotr.kliczew...@gmail.com> _______________________________________________ vdsm-patches mailing list vdsm-patches@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches