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

Reply via email to