Python API: Re-adding rudimentary variable length array pack support.
[vpp.git] / vpp-api / python / vpp_papi / vpp_papi.py
1 #
2 # Copyright (c) 2016 Cisco and/or its affiliates.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 #
16 # Import C API shared object
17 #
18 from __future__ import print_function
19
20 import signal, os, sys
21 from struct import *
22
23 scriptdir = os.path.dirname(os.path.realpath(__file__))
24 sys.path.append(scriptdir)
25 import vpp_api
26 from vpp_api_base import *
27
28 # Import API definitions. The core VPE API is imported into main namespace
29 import memclnt
30 from vpe import *
31 vpe = sys.modules['vpe']
32
33 def eprint(*args, **kwargs):
34     print(*args, file=sys.stderr, **kwargs)
35
36 def msg_handler(msg):
37     if not msg:
38         eprint('vpp_api.read failed')
39         return
40
41     id = unpack('>H', msg[0:2])
42     if id[0] == memclnt.VL_API_RX_THREAD_EXIT:
43         return;
44
45     #
46     # Decode message and returns a tuple.
47     #
48     try:
49         r = api_func_table[id[0]](msg)
50     except:
51         eprint('Message decode failed', id[0], api_func_table[id[0]])
52         raise
53
54     if 'context' in r._asdict():
55         if r.context > 0:
56             context = r.context
57
58     #
59     # XXX: Call provided callback for event
60     # Are we guaranteed to not get an event during processing of other messages?
61     # How to differentiate what's a callback message and what not? Context = 0?
62     #
63     if not is_waiting_for_reply():
64         event_callback_call(r)
65         return
66
67     #
68     # Collect results until control ping
69     #
70     if id[0] == vpe.VL_API_CONTROL_PING_REPLY:
71         results_event_set(context)
72         waiting_for_reply_clear()
73         return
74     if not is_results_context(context):
75         eprint('Not expecting results for this context', context)
76         return
77     if is_results_more(context):
78         results_append(context, r)
79         return
80
81     results_set(context, r)
82     results_event_set(context)
83     waiting_for_reply_clear()
84
85 def connect(name):
86     signal.alarm(3) # 3 second
87     rv = vpp_api.connect(name, msg_handler)
88     signal.alarm(0)
89
90     #
91     # Assign message id space for plugins
92     #
93     plugin_map_plugins()
94
95     return rv
96
97 def disconnect():
98     rv = vpp_api.disconnect()
99     return rv
100
101 # CLI convenience wrapper
102 def cli_exec(cmd):
103     cmd += '\n'
104     r = cli_inband(len(cmd), cmd)
105     return r.reply[0].decode().rstrip('\x00')
106
107 def register_event_callback(callback):
108     event_callback_set(callback)
109
110 def plugin_name_to_id(plugin, name_to_id_table, base):
111     try:
112         m = globals()[plugin]
113     except KeyError:
114         m = sys.modules[plugin]
115     for name in name_to_id_table:
116         setattr(m, name, name_to_id_table[name] + base)
117
118 def plugin_map_plugins():
119     for p in plugins:
120         if p == 'memclnt' or p == 'vpe':
121             continue
122
123         #
124         # Find base
125         # Update api table
126         #
127         version = plugins[p]['version']
128         name = p + '_' + format(version, '08x')
129         r = memclnt.get_first_msg_id(name.encode('ascii'))
130         ## TODO: Add error handling / raise exception
131         if r.retval != 0:
132             eprint('Failed getting first msg id for:', p)
133             continue
134
135         # Set base
136         base = r.first_msg_id
137         msg_id_base_set = plugins[p]['msg_id_base_set']
138         msg_id_base_set(base)
139         plugins[p]['base'] = base
140         func_table = plugins[p]['func_table']
141         i = r.first_msg_id
142         # Insert doesn't extend the table
143         if i + len(func_table) > len(api_func_table):
144             fill = [None] * (i + len(func_table) - len(api_func_table))
145             api_func_table.extend(fill)
146         for entry in func_table:
147             api_func_table[i] = entry
148             i += 1
149         plugin_name_to_id(p, plugins[p]['name_to_id_table'], base)
150
151 #
152 # Set up core API
153 #
154 memclnt.msg_id_base_set(1)
155 plugins['memclnt']['base'] = 1
156 msg_id_base_set(len(plugins['memclnt']['func_table']) + 1)
157 plugins['vpe']['base'] = len(plugins['memclnt']['func_table']) + 1
158 api_func_table = []
159 api_func_table.append(None)
160 api_func_table[1:] = plugins['memclnt']['func_table'] + plugins['vpe']['func_table']
161 plugin_name_to_id('memclnt', plugins['memclnt']['name_to_id_table'], 1)
162 plugin_name_to_id('vpe', plugins['vpe']['name_to_id_table'], plugins['vpe']['base'])