aboutsummaryrefslogtreecommitdiffstats
path: root/resources/tools/papi
diff options
context:
space:
mode:
Diffstat (limited to 'resources/tools/papi')
-rw-r--r--resources/tools/papi/vpp_papi_provider.py230
1 files changed, 230 insertions, 0 deletions
diff --git a/resources/tools/papi/vpp_papi_provider.py b/resources/tools/papi/vpp_papi_provider.py
new file mode 100644
index 0000000000..0d934977ce
--- /dev/null
+++ b/resources/tools/papi/vpp_papi_provider.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Python API provider.
+"""
+
+import argparse
+import binascii
+import fnmatch
+import json
+import os
+import sys
+
+sys.path.append('/tmp/openvpp-testing')
+try:
+ from resources.libraries.python.PapiErrors import *
+except:
+ raise
+
+# Sphinx creates auto-generated documentation by importing the python source
+# files and collecting the docstrings from them. The NO_VPP_PAPI flag allows
+# the vpp_papi_provider.py file to be importable without having to build
+# the whole vpp api if the user only wishes to generate the test documentation.
+do_import = True
+try:
+ no_vpp_papi = os.getenv("NO_VPP_PAPI")
+ if no_vpp_papi == "1":
+ do_import = False
+except:
+ pass
+
+if do_import:
+ # TODO: run os.walk once per whole suite and store the path in environmental
+ # variable
+ modules_path = None
+ for root, dirs, files in os.walk('/usr/lib'):
+ for name in files:
+ if name == 'vpp_papi.py':
+ modules_path = os.path.split(root)[0]
+ break
+ if modules_path:
+ sys.path.append(modules_path)
+ from vpp_papi import VPP
+ else:
+ raise PapiInitError('vpp_papi module not found')
+
+# client name
+CLIENT_NAME = 'csit_papi'
+
+
+def papi_init(vpp_json_dir='/usr/share/vpp/api/'):
+ """Construct a VPP instance from VPP JSON API files.
+
+ :param vpp_json_dir: Directory containing all the JSON API files. If VPP is
+ installed in the system it will be in /usr/share/vpp/api/.
+ :type vpp_json_dir: str
+ :returns: VPP instance.
+ :rtype: VPP object
+ :raises PapiJsonFileError: If no api.json file found.
+ :raises PapiInitError: If PAPI initialization failed.
+ """
+ # construct a list of all the json api files
+ jsonfiles = []
+ for root, dirnames, filenames in os.walk(vpp_json_dir):
+ for filename in fnmatch.filter(filenames, '*.api.json'):
+ jsonfiles.append(os.path.join(vpp_json_dir, filename))
+ if not jsonfiles:
+ raise PapiJsonFileError(
+ 'No json api files found in location {dir}'.format(
+ dir=vpp_json_dir))
+
+ try:
+ vpp = VPP(jsonfiles)
+ return vpp
+ except Exception as err:
+ raise PapiInitError('PAPI init failed:\n{exc}'.format(exc=repr(err)))
+
+
+def papi_connect(vpp_client, name='vpp_api'):
+ """Attach to VPP client.
+
+ :param vpp_client: VPP instance to connect to.
+ :param name: VPP client name.
+ :type vpp_client: VPP object
+ :type name: str
+ :returns: Return code of VPP.connect() method.
+ :rtype: int
+ """
+ return vpp_client.connect(name)
+
+
+def papi_disconnect(vpp_client):
+ """Detach from VPP client.
+
+ :param vpp_client: VPP instance to detach from.
+ :type vpp_client: VPP object
+ """
+ vpp_client.disconnect()
+
+
+def papi_run(vpp_client, api_name, api_args):
+ """api_name
+
+ :param vpp_client: VPP instance.
+ :param api_name: VPP API name.
+ :param api_args: Input arguments of the API.
+ :type vpp_client: VPP object
+ :type api_name: str
+ :type api_args: dict
+ :returns: VPP API reply.
+ :rtype: Vpp_serializer reply object
+ """
+ papi_fn = getattr(vpp_client.api, api_name)
+ return papi_fn(**api_args)
+
+
+def convert_reply(api_r):
+ """Process API reply / a part of API reply for smooth converting to
+ JSON string.
+
+ Apply binascii.hexlify() method for string values.
+ :param api_r: API reply.
+ :type api_r: Vpp_serializer reply object (named tuple)
+ :returns: Processed API reply / a part of API reply.
+ :rtype: dict
+ """
+ unwanted_fields = ['count', 'index']
+
+ 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:
+ attr_value = getattr(api_r, item)
+ value = binascii.hexlify(attr_value) \
+ if isinstance(attr_value, str) else attr_value
+ reply_value[item] = value
+ reply_dict[reply_key] = reply_value
+ return reply_dict
+
+
+def process_reply(api_reply):
+ """Process API reply for smooth converting to JSON string.
+
+ :param api_reply: API reply.
+ :type api_reply: Vpp_serializer reply object (named tuple) or list of
+ vpp_serializer reply objects
+ :returns: Processed API reply.
+ :rtype: list or dict
+ """
+
+ if isinstance(api_reply, list):
+ converted_reply = list()
+ for r in api_reply:
+ converted_reply.append(convert_reply(r))
+ else:
+ converted_reply = convert_reply(api_reply)
+ return converted_reply
+
+
+def main():
+ """Main function for the Python API provider.
+
+ :raises PapiCommandInputError: If invalid attribute name or invalid value is
+ used in API call.
+ :raises PapiCommandError: If PAPI command(s) execution failed.
+ """
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-j", "--json_data",
+ required=True,
+ type=str,
+ help="JSON string (list) containing API name(s) and "
+ "its/their input argument(s).")
+ parser.add_argument("-d", "--json_dir",
+ type=str,
+ default='/usr/share/vpp/api/',
+ help="Directory containing all vpp json api files.")
+ args = parser.parse_args()
+ json_string = args.json_data
+ vpp_json_dir = args.json_dir
+
+ vpp = papi_init(vpp_json_dir=vpp_json_dir)
+
+ reply = list()
+ json_data = json.loads(json_string)
+ papi_connect(vpp, CLIENT_NAME)
+ for data in json_data:
+ api_name = data['api_name']
+ api_args_unicode = data['api_args']
+ api_reply = dict(api_name=api_name)
+ api_args = dict()
+ for a_k, a_v in api_args_unicode.iteritems():
+ value = binascii.unhexlify(a_v) if isinstance(a_v, unicode) else a_v
+ api_args[str(a_k)] = value
+ try:
+ rep = papi_run(vpp, api_name, api_args)
+ api_reply['api_reply'] = process_reply(rep)
+ reply.append(api_reply)
+ except (AttributeError, ValueError) as err:
+ papi_disconnect(vpp)
+ raise PapiCommandInputError(
+ 'PAPI command {api}({args}) input error:\n{exc}'.format(
+ api=api_name, args=api_args), exc=repr(err))
+ except Exception as err:
+ papi_disconnect(vpp)
+ raise PapiCommandError(
+ 'PAPI command {api}({args}) error:\n{exc}'.format(
+ api=api_name, args=api_args), exc=repr(err))
+ papi_disconnect(vpp)
+
+ return json.dumps(reply)
+
+
+if __name__ == '__main__':
+ sys.stdout.write(main())
+ sys.stdout.flush()
+ sys.exit(0)