Python API: Add support for shared memory prefix
[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 import vpp_api
24 from vpp_api_base import *
25
26 # Import API definitions. The core VPE API is imported into main namespace
27 import memclnt
28
29 # Cheating a bit, importing it into this namespace as well as a module.
30 import vpe
31 from vpe import *
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] == 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 handler(signum, frame):
86     print('Signal handler called with signal', signum)
87     raise IOError("Couldn't connect to VPP!")
88
89 def connect(name, chroot_prefix = None):
90     # Set the signal handler
91     signal.signal(signal.SIGALRM, handler)
92
93     signal.alarm(3) # 3 second
94     if not chroot_prefix:
95         rv = vpp_api.connect(name, msg_handler)
96     else:
97         rv = vpp_api.connect(name, msg_handler, chroot_prefix)
98
99     signal.alarm(0)
100
101     #
102     # Assign message id space for plugins
103     #
104     try:
105         plugin_map_plugins()
106     except:
107         return -1
108     return rv
109
110 def disconnect():
111     rv = vpp_api.disconnect()
112     return rv
113
114 def register_event_callback(callback):
115     event_callback_set(callback)
116
117 def plugin_name_to_id(plugin, name_to_id_table, base):
118     try:
119         m = globals()[plugin]
120     except KeyError:
121         m = sys.modules[plugin]
122
123     for name in name_to_id_table:
124         setattr(m, name, name_to_id_table[name] + base)
125
126 def plugin_map_plugins():
127     for p in plugins:
128         if p == 'memclnt' or p == 'vpe':
129             continue
130
131         #
132         # Find base
133         # Update api table
134         #
135         version = plugins[p]['version']
136         name = p + '_' + format(version, '08x')
137         r = memclnt.get_first_msg_id(name)
138         if r.retval != 0:
139             eprint('Failed getting first msg id for:', p, r, name)
140             raise
141
142         # Set base
143         base = r.first_msg_id
144         msg_id_base_set = plugins[p]['msg_id_base_set']
145         msg_id_base_set(base)
146         plugins[p]['base'] = base
147         func_table = plugins[p]['func_table']
148         i = r.first_msg_id
149         # Insert doesn't extend the table
150         if i + len(func_table) > len(api_func_table):
151             fill = [None] * (i + len(func_table) - len(api_func_table))
152             api_func_table.extend(fill)
153         for entry in func_table:
154             api_func_table[i] = entry
155             i += 1
156         plugin_name_to_id(p, plugins[p]['name_to_id_table'], base)
157
158 #
159 # Set up core API
160 #
161 memclnt.msg_id_base_set(1)
162 plugins['memclnt']['base'] = 1
163
164 # vpe
165 msg_id_base_set(len(plugins['memclnt']['func_table']) + 1)
166 plugins['vpe']['base'] = len(plugins['memclnt']['func_table']) + 1
167 api_func_table = []
168 api_func_table.append(None)
169 api_func_table[1:] = plugins['memclnt']['func_table'] + plugins['vpe']['func_table']
170 plugin_name_to_id('memclnt', plugins['memclnt']['name_to_id_table'], 1)
171 plugin_name_to_id('vpe', plugins['vpe']['name_to_id_table'], plugins['vpe']['base'])
172 plugin_name_to_id(__name__, plugins['vpe']['name_to_id_table'], plugins['vpe']['base'])