3 # Copyright (c) 2018 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """Python API provider.
26 sys.path.append('/tmp/openvpp-testing')
28 from resources.libraries.python.PapiErrors import *
32 # Sphinx creates auto-generated documentation by importing the python source
33 # files and collecting the docstrings from them. The NO_VPP_PAPI flag allows
34 # the vpp_papi_provider.py file to be importable without having to build
35 # the whole vpp api if the user only wishes to generate the test documentation.
38 no_vpp_papi = os.getenv("NO_VPP_PAPI")
39 if no_vpp_papi == "1":
45 # TODO: run os.walk once per whole suite and store the path in environmental
48 for root, dirs, files in os.walk('/usr/lib'):
50 if name == 'vpp_papi.py':
51 modules_path = os.path.split(root)[0]
54 sys.path.append(modules_path)
55 from vpp_papi import VPP
57 raise PapiInitError('vpp_papi module not found')
60 CLIENT_NAME = 'csit_papi'
63 def papi_init(vpp_json_dir='/usr/share/vpp/api/'):
64 """Construct a VPP instance from VPP JSON API files.
66 :param vpp_json_dir: Directory containing all the JSON API files. If VPP is
67 installed in the system it will be in /usr/share/vpp/api/.
68 :type vpp_json_dir: str
69 :returns: VPP instance.
71 :raises PapiJsonFileError: If no api.json file found.
72 :raises PapiInitError: If PAPI initialization failed.
74 # construct a list of all the json api files
76 for root, dirnames, filenames in os.walk(vpp_json_dir):
77 for filename in fnmatch.filter(filenames, '*.api.json'):
78 jsonfiles.append(os.path.join(vpp_json_dir, filename))
80 raise PapiJsonFileError(
81 'No json api files found in location {dir}'.format(
87 except Exception as err:
88 raise PapiInitError('PAPI init failed:\n{exc}'.format(exc=repr(err)))
91 def papi_connect(vpp_client, name='vpp_api'):
92 """Attach to VPP client.
94 :param vpp_client: VPP instance to connect to.
95 :param name: VPP client name.
96 :type vpp_client: VPP object
98 :returns: Return code of VPP.connect() method.
101 return vpp_client.connect(name)
104 def papi_disconnect(vpp_client):
105 """Detach from VPP client.
107 :param vpp_client: VPP instance to detach from.
108 :type vpp_client: VPP object
110 vpp_client.disconnect()
113 def papi_run(vpp_client, api_name, api_args):
116 :param vpp_client: VPP instance.
117 :param api_name: VPP API name.
118 :param api_args: Input arguments of the API.
119 :type vpp_client: VPP object
122 :returns: VPP API reply.
123 :rtype: Vpp_serializer reply object
125 papi_fn = getattr(vpp_client.api, api_name)
126 return papi_fn(**api_args)
129 def convert_reply(api_r):
130 """Process API reply / a part of API reply for smooth converting to
133 # Apply binascii.hexlify() method for string values.
134 :param api_r: API reply.
135 :type api_r: Vpp_serializer reply object (named tuple)
136 :returns: Processed API reply / a part of API reply.
139 unwanted_fields = ['count', 'index']
142 reply_key = repr(api_r).split('(')[0]
144 for item in dir(api_r):
145 if not item.startswith('_') and item not in unwanted_fields:
146 # attr_value = getattr(api_r, item)
147 # value = binascii.hexlify(attr_value) \
148 # if isinstance(attr_value, str) else attr_value
149 value = getattr(api_r, item)
150 reply_value[item] = value
151 reply_dict[reply_key] = reply_value
155 def process_reply(api_reply):
156 """Process API reply for smooth converting to JSON string.
158 :param api_reply: API reply.
159 :type api_reply: Vpp_serializer reply object (named tuple) or list of
160 vpp_serializer reply objects
161 :returns: Processed API reply.
165 if isinstance(api_reply, list):
166 converted_reply = list()
168 converted_reply.append(convert_reply(r))
170 converted_reply = convert_reply(api_reply)
171 return converted_reply
175 """Main function for the Python API provider.
177 :raises PapiCommandInputError: If invalid attribute name or invalid value is
179 :raises PapiCommandError: If PAPI command(s) execution failed.
182 parser = argparse.ArgumentParser()
183 parser.add_argument("-j", "--json_data",
186 help="JSON string (list) containing API name(s) and "
187 "its/their input argument(s).")
188 parser.add_argument("-d", "--json_dir",
190 default='/usr/share/vpp/api/',
191 help="Directory containing all vpp json api files.")
192 args = parser.parse_args()
193 json_string = args.json_data
194 vpp_json_dir = args.json_dir
196 vpp = papi_init(vpp_json_dir=vpp_json_dir)
199 json_data = json.loads(json_string)
200 papi_connect(vpp, CLIENT_NAME)
201 for data in json_data:
202 api_name = data['api_name']
203 api_args_unicode = data['api_args']
204 api_reply = dict(api_name=api_name)
206 for a_k, a_v in api_args_unicode.iteritems():
207 value = binascii.unhexlify(a_v) if isinstance(a_v, unicode) else a_v
208 api_args[str(a_k)] = value
210 rep = papi_run(vpp, api_name, api_args)
211 api_reply['api_reply'] = process_reply(rep)
212 reply.append(api_reply)
213 except (AttributeError, ValueError) as err:
215 raise PapiCommandInputError(
216 'PAPI command {api}({args}) input error:\n{exc}'.format(
217 api=api_name, args=api_args), exc=repr(err))
218 except Exception as err:
220 raise PapiCommandError(
221 'PAPI command {api}({args}) error:\n{exc}'.format(
222 api=api_name, args=api_args), exc=repr(err))
225 return json.dumps(reply)
228 if __name__ == '__main__':
229 sys.stdout.write(main())