3 # Copyright (c) 2019 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.
25 # Sphinx creates auto-generated documentation by importing the python source
26 # files and collecting the docstrings from them. The NO_VPP_PAPI flag allows
27 # the vpp_papi_provider.py file to be importable without having to build
28 # the whole vpp api if the user only wishes to generate the test documentation.
31 no_vpp_papi = os.getenv("NO_VPP_PAPI")
32 if no_vpp_papi == "1":
38 # TODO: run os.walk once per whole suite and store the path in environmental
41 for root, dirs, files in os.walk('/usr/lib'):
43 if name == 'vpp_papi.py':
44 modules_path = os.path.split(root)[0]
47 sys.path.append(modules_path)
48 from vpp_papi import VPP
50 raise RuntimeError('vpp_papi module not found')
53 CLIENT_NAME = 'csit_papi'
57 """Construct a VPP instance from VPP JSON API files.
59 :param vpp_json_dir: Directory containing all the JSON API files. If VPP is
60 installed in the system it will be in /usr/share/vpp/api/.
61 :type vpp_json_dir: str
62 :returns: VPP instance.
64 :raises PapiJsonFileError: If no api.json file found.
65 :raises PapiInitError: If PAPI initialization failed.
70 except Exception as err:
71 raise RuntimeError('PAPI init failed:\n{err}'.format(err=repr(err)))
74 def papi_connect(vpp_client, name='vpp_api'):
75 """Attach to VPP client.
77 :param vpp_client: VPP instance to connect to.
78 :param name: VPP client name.
79 :type vpp_client: VPP object
81 :returns: Return code of VPP.connect() method.
84 return vpp_client.connect(name)
87 def papi_disconnect(vpp_client):
88 """Detach from VPP client.
90 :param vpp_client: VPP instance to detach from.
91 :type vpp_client: VPP object
93 vpp_client.disconnect()
96 def papi_run(vpp_client, api_name, api_args):
99 :param vpp_client: VPP instance.
100 :param api_name: VPP API name.
101 :param api_args: Input arguments of the API.
102 :type vpp_client: VPP object
105 :returns: VPP API reply.
106 :rtype: Vpp_serializer reply object
108 papi_fn = getattr(vpp_client.api, api_name)
109 return papi_fn(**api_args)
112 def convert_reply(api_r):
113 """Process API reply / a part of API reply for smooth converting to
116 Apply binascii.hexlify() method for string values.
118 :param api_r: API reply.
119 :type api_r: Vpp_serializer reply object (named tuple)
120 :returns: Processed API reply / a part of API reply.
123 unwanted_fields = ['count', 'index']
126 reply_key = repr(api_r).split('(')[0]
128 for item in dir(api_r):
129 if not item.startswith('_') and item not in unwanted_fields:
130 # attr_value = getattr(api_r, item)
131 # value = binascii.hexlify(attr_value) \
132 # if isinstance(attr_value, str) else attr_value
133 value = getattr(api_r, item)
134 reply_value[item] = value
135 reply_dict[reply_key] = reply_value
139 def process_reply(api_reply):
140 """Process API reply for smooth converting to JSON string.
142 :param api_reply: API reply.
143 :type api_reply: Vpp_serializer reply object (named tuple) or list of
144 vpp_serializer reply objects
145 :returns: Processed API reply.
149 if isinstance(api_reply, list):
150 converted_reply = list()
152 converted_reply.append(convert_reply(r))
154 converted_reply = convert_reply(api_reply)
155 return converted_reply
159 """Main function for the Python API provider.
161 :raises RuntimeError: If invalid attribute name or invalid value is
162 used in API call or if PAPI command(s) execution failed.
165 parser = argparse.ArgumentParser()
166 parser.add_argument("-j", "--json_data",
169 help="JSON string (list) containing API name(s) and "
170 "its/their input argument(s).")
171 parser.add_argument("-d", "--json_dir",
173 default='/usr/share/vpp/api/',
174 help="Directory containing all vpp json api files.")
175 args = parser.parse_args()
176 json_string = args.json_data
181 json_data = json.loads(json_string)
182 papi_connect(vpp, CLIENT_NAME)
183 for data in json_data:
184 api_name = data['api_name']
185 api_args_unicode = data['api_args']
186 api_reply = dict(api_name=api_name)
188 for a_k, a_v in api_args_unicode.items():
189 value = binascii.unhexlify(a_v) if isinstance(a_v, unicode) else a_v
190 api_args[str(a_k)] = value
192 rep = papi_run(vpp, api_name, api_args)
193 api_reply['api_reply'] = process_reply(rep)
194 reply.append(api_reply)
195 except (AttributeError, ValueError) as err:
197 raise RuntimeError('PAPI command {api}({args}) input error:\n{err}'.
201 except Exception as err:
203 raise RuntimeError('PAPI command {api}({args}) error:\n{exc}'.
209 return json.dumps(reply)
212 if __name__ == '__main__':
213 sys.stdout.write(main())