CSIT-1407 FIX vpp install after VPP changes
[csit.git] / resources / tools / papi / vpp_papi_provider.py
1 #!/usr/bin/env python
2
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15
16 """Python API provider.
17 """
18
19 import argparse
20 import binascii
21 import fnmatch
22 import json
23 import os
24 import sys
25
26 sys.path.append('/tmp/openvpp-testing')
27 try:
28     from resources.libraries.python.PapiErrors import *
29 except:
30     raise
31
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.
36 do_import = True
37 try:
38     no_vpp_papi = os.getenv("NO_VPP_PAPI")
39     if no_vpp_papi == "1":
40         do_import = False
41 except:
42     pass
43
44 if do_import:
45     # TODO: run os.walk once per whole suite and store the path in environmental
46     # variable
47     modules_path = None
48     for root, dirs, files in os.walk('/usr/lib'):
49         for name in files:
50             if name == 'vpp_papi.py':
51                 modules_path = os.path.split(root)[0]
52                 break
53     if modules_path:
54         sys.path.append(modules_path)
55         from vpp_papi import VPP
56     else:
57         raise PapiInitError('vpp_papi module not found')
58
59 # client name
60 CLIENT_NAME = 'csit_papi'
61
62
63 def papi_init():
64     """Construct a VPP instance from VPP JSON API files.
65
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.
70     :rtype: VPP object
71     :raises PapiJsonFileError: If no api.json file found.
72     :raises PapiInitError: If PAPI initialization failed.
73     """
74     try:
75         vpp = VPP()
76         return vpp
77     except Exception as err:
78         raise PapiInitError('PAPI init failed:\n{exc}'.format(exc=repr(err)))
79
80
81 def papi_connect(vpp_client, name='vpp_api'):
82     """Attach to VPP client.
83
84     :param vpp_client: VPP instance to connect to.
85     :param name: VPP client name.
86     :type vpp_client: VPP object
87     :type name: str
88     :returns: Return code of VPP.connect() method.
89     :rtype: int
90     """
91     return vpp_client.connect(name)
92
93
94 def papi_disconnect(vpp_client):
95     """Detach from VPP client.
96
97     :param vpp_client: VPP instance to detach from.
98     :type vpp_client: VPP object
99     """
100     vpp_client.disconnect()
101
102
103 def papi_run(vpp_client, api_name, api_args):
104     """api_name
105
106     :param vpp_client: VPP instance.
107     :param api_name: VPP API name.
108     :param api_args: Input arguments of the API.
109     :type vpp_client: VPP object
110     :type api_name: str
111     :type api_args: dict
112     :returns: VPP API reply.
113     :rtype: Vpp_serializer reply object
114     """
115     papi_fn = getattr(vpp_client.api, api_name)
116     return papi_fn(**api_args)
117
118
119 def convert_reply(api_r):
120     """Process API reply / a part of API reply for smooth converting to
121     JSON string.
122
123     # Apply binascii.hexlify() method for string values.
124     :param api_r: API reply.
125     :type api_r: Vpp_serializer reply object (named tuple)
126     :returns: Processed API reply / a part of API reply.
127     :rtype: dict
128     """
129     unwanted_fields = ['count', 'index']
130
131     reply_dict = dict()
132     reply_key = repr(api_r).split('(')[0]
133     reply_value = dict()
134     for item in dir(api_r):
135         if not item.startswith('_') and item not in unwanted_fields:
136             # attr_value = getattr(api_r, item)
137             # value = binascii.hexlify(attr_value) \
138             #     if isinstance(attr_value, str) else attr_value
139             value = getattr(api_r, item)
140             reply_value[item] = value
141     reply_dict[reply_key] = reply_value
142     return reply_dict
143
144
145 def process_reply(api_reply):
146     """Process API reply for smooth converting to JSON string.
147
148     :param api_reply: API reply.
149     :type api_reply: Vpp_serializer reply object (named tuple) or list of
150         vpp_serializer reply objects
151     :returns: Processed API reply.
152     :rtype: list or dict
153     """
154
155     if isinstance(api_reply, list):
156         converted_reply = list()
157         for r in api_reply:
158             converted_reply.append(convert_reply(r))
159     else:
160         converted_reply = convert_reply(api_reply)
161     return converted_reply
162
163
164 def main():
165     """Main function for the Python API provider.
166
167     :raises PapiCommandInputError: If invalid attribute name or invalid value is
168         used in API call.
169     :raises PapiCommandError: If PAPI command(s) execution failed.
170     """
171
172     parser = argparse.ArgumentParser()
173     parser.add_argument("-j", "--json_data",
174                         required=True,
175                         type=str,
176                         help="JSON string (list) containing API name(s) and "
177                              "its/their input argument(s).")
178     parser.add_argument("-d", "--json_dir",
179                         type=str,
180                         default='/usr/share/vpp/api/',
181                         help="Directory containing all vpp json api files.")
182     args = parser.parse_args()
183     json_string = args.json_data
184
185     vpp = papi_init()
186
187     reply = list()
188     json_data = json.loads(json_string)
189     papi_connect(vpp, CLIENT_NAME)
190     for data in json_data:
191         api_name = data['api_name']
192         api_args_unicode = data['api_args']
193         api_reply = dict(api_name=api_name)
194         api_args = dict()
195         for a_k, a_v in api_args_unicode.iteritems():
196             value = binascii.unhexlify(a_v) if isinstance(a_v, unicode) else a_v
197             api_args[str(a_k)] = value
198         try:
199             rep = papi_run(vpp, api_name, api_args)
200             api_reply['api_reply'] = process_reply(rep)
201             reply.append(api_reply)
202         except (AttributeError, ValueError) as err:
203             papi_disconnect(vpp)
204             raise PapiCommandInputError(
205                 'PAPI command {api}({args}) input error:\n{exc}'.format(
206                     api=api_name, args=api_args), exc=repr(err))
207         except Exception as err:
208             papi_disconnect(vpp)
209             raise PapiCommandError(
210                 'PAPI command {api}({args}) error:\n{exc}'.format(
211                     api=api_name, args=api_args), exc=repr(err))
212     papi_disconnect(vpp)
213
214     return json.dumps(reply)
215
216
217 if __name__ == '__main__':
218     sys.stdout.write(main())
219     sys.stdout.flush()
220     sys.exit(0)