Add support for more complex structures in PAPI calls/replies
[csit.git] / resources / tools / papi / vpp_papi_provider.py
index 299cd2c..1136be5 100755 (executable)
@@ -91,12 +91,33 @@ def _convert_reply(api_r):
     """
     unwanted_fields = ['count', 'index', 'context']
 
+    def process_value(val):
+        if isinstance(val, dict):
+            val_dict = dict()
+            for val_k, val_v in val.iteritems():
+                val_dict[str(val_k)] = process_value(val_v)
+            return val_dict
+        elif isinstance(val, list):
+            for idx, val_l in enumerate(val):
+                val[idx] = process_value(val_l)
+            return val
+        elif hasattr(val, '__int__'):
+            return int(val)
+        elif hasattr(val, '__str__'):
+            return binascii.hexlify(str(val))
+        # Next handles parameters not supporting preferred integer or string
+        # representation to get it logged
+        elif hasattr(val, '__repr__'):
+            return repr(val)
+        else:
+            return val
+
     reply_dict = dict()
     reply_key = repr(api_r).split('(')[0]
     reply_value = dict()
     for item in dir(api_r):
         if not item.startswith('_') and item not in unwanted_fields:
-            reply_value[item] = getattr(api_r, item)
+            reply_value[item] = process_value(getattr(api_r, item))
     reply_dict[reply_key] = reply_value
     return reply_dict
 
@@ -118,6 +139,23 @@ def process_json_request(args):
 
     reply = list()
 
+    def process_value(val):
+        if isinstance(val, dict):
+            val_dict = dict()
+            for val_k, val_v in val.iteritems():
+                val_dict[str(val_k)] = process_value(val_v)
+            return val_dict
+        elif isinstance(val, list):
+            for idx, item in enumerate(val):
+                val[idx] = process_value(item)
+            return val
+        elif isinstance(val, unicode):
+            return binascii.unhexlify(val)
+        elif isinstance(val, int):
+            return val
+        else:
+            return str(val)
+
     json_data = json.loads(args.data)
     vpp.connect(CLIENT_NAME)
     for data in json_data:
@@ -126,8 +164,7 @@ def process_json_request(args):
         api_reply = dict(api_name=api_name)
         api_args = dict()
         for a_k, a_v in api_args_unicode.items():
-            value = binascii.unhexlify(a_v) if isinstance(a_v, unicode) else a_v
-            api_args[str(a_k)] = value if isinstance(value, int) else str(value)
+            api_args[str(a_k)] = process_value(a_v)
         try:
             papi_fn = getattr(vpp.api, api_name)
             rep = papi_fn(**api_args)
@@ -182,6 +219,37 @@ def process_stats(args):
         data = stats.dump(directory)
         reply.append(data)
 
+    try:
+        return json.dumps(reply)
+    except UnicodeDecodeError as err:
+        raise RuntimeError('PAPI reply {reply} error:\n{exc}'.format(
+            reply=reply, exc=repr(err)))
+
+
+def process_stats_request(args):
+    """Process the VPP Stats requests.
+
+    :param args: Command line arguments passed to VPP PAPI Provider.
+    :type args: ArgumentParser
+    :returns: JSON formatted string.
+    :rtype: str
+    :raises RuntimeError: If PAPI command error occurs.
+    """
+
+    try:
+        stats = VPPStats(args.socket)
+    except Exception as err:
+        raise RuntimeError('PAPI init failed:\n{err}'.format(err=repr(err)))
+
+    try:
+        json_data = json.loads(args.data)
+    except ValueError as err:
+        raise RuntimeError('Input json string is invalid:\n{err}'.
+                           format(err=repr(err)))
+
+    papi_fn = getattr(stats, json_data["api_name"])
+    reply = papi_fn(**json_data.get("api_args", {}))
+
     return json.dumps(reply)
 
 
@@ -193,7 +261,8 @@ def main():
     process_request = dict(
         request=process_json_request,
         dump=process_json_request,
-        stats=process_stats
+        stats=process_stats,
+        stats_request=process_stats_request
     )
 
     parser = argparse.ArgumentParser(