a7ba26b4722d3aa371d48707190c2500be262faf
[vpp.git] / src / vpp-api / python / vpp_papi / vpp_transport_shmem.py
1 #
2 # A transport class. With two implementations.
3 # One for socket and one for shared memory.
4 #
5
6 from cffi import FFI
7 import cffi
8
9 ffi = FFI()
10 ffi.cdef("""
11 typedef void (*vac_callback_t)(unsigned char * data, int len);
12 typedef void (*vac_error_callback_t)(void *, unsigned char *, int);
13 int vac_connect(char * name, char * chroot_prefix, vac_callback_t cb,
14     int rx_qlen);
15 int vac_disconnect(void);
16 int vac_read(char **data, int *l, unsigned short timeout);
17 int vac_write(char *data, int len);
18 void vac_free(void * msg);
19
20 int vac_get_msg_index(unsigned char * name);
21 int vac_msg_table_size(void);
22 int vac_msg_table_max_index(void);
23
24 void vac_rx_suspend (void);
25 void vac_rx_resume (void);
26 void vac_set_error_handler(vac_error_callback_t);
27 void vac_mem_init (size_t size);
28 """)
29
30 vpp_object = None
31
32 # allow file to be imported so it can be mocked in tests.
33 # If the shared library fails, VppTransport cannot be initialized.
34 try:
35     vpp_api = ffi.dlopen('libvppapiclient.so')
36 except OSError:
37     vpp_api = None
38
39
40 @ffi.callback("void(unsigned char *, int)")
41 def vac_callback_sync(data, len):
42     vpp_object.msg_handler_sync(ffi.buffer(data, len))
43
44
45 @ffi.callback("void(unsigned char *, int)")
46 def vac_callback_async(data, len):
47     vpp_object.msg_handler_async(ffi.buffer(data, len))
48
49
50 @ffi.callback("void(void *, unsigned char *, int)")
51 def vac_error_handler(arg, msg, msg_len):
52     vpp_object.logger.warning("VPP API client:: %s", ffi.string(msg, msg_len))
53
54
55 class VppTransportShmemIOError(IOError):
56     """ exception communicating with vpp over shared memory """
57
58     def __init__(self, rv, descr):
59         self.rv = rv
60         self.desc = descr
61
62         super(VppTransportShmemIOError, self).__init__(rv, descr)
63
64
65 class VppTransport(object):
66     VppTransportShmemIOError = VppTransportShmemIOError
67
68     def __init__(self, parent, read_timeout, server_address):
69         self.connected = False
70         self.read_timeout = read_timeout
71         self.parent = parent
72         global vpp_object
73         vpp_object = parent
74
75         vpp_api.vac_mem_init(0)
76
77         # Register error handler
78         vpp_api.vac_set_error_handler(vac_error_handler)
79
80         # Support legacy CFFI
81         # from_buffer supported from 1.8.0
82         (major, minor, patch) = [int(s) for s in
83                                  cffi.__version__.split('.', 3)]
84         if major >= 1 and minor >= 8:
85             self.write = self._write_new_cffi
86         else:
87             self.write = self._write_legacy_cffi
88
89     def connect(self, name, pfx, msg_handler, rx_qlen):
90         self.connected = True
91         if not pfx:
92             pfx = ffi.NULL
93         return vpp_api.vac_connect(name.encode('ascii'), pfx, msg_handler, rx_qlen)
94
95     def disconnect(self):
96         self.connected = False
97         return vpp_api.vac_disconnect()
98
99     def suspend(self):
100         vpp_api.vac_rx_suspend()
101
102     def resume(self):
103         vpp_api.vac_rx_resume()
104
105     def get_callback(self, do_async):
106         return vac_callback_sync if not do_async else vac_callback_async
107
108     def get_msg_index(self, name):
109         return vpp_api.vac_get_msg_index(name.encode('ascii'))
110
111     def msg_table_max_index(self):
112         return vpp_api.vac_msg_table_max_index()
113
114     def _write_new_cffi(self, buf):
115         """Send a binary-packed message to VPP."""
116         if not self.connected:
117             raise VppTransportShmemIOError(1, 'Not connected')
118         return vpp_api.vac_write(ffi.from_buffer(buf), len(buf))
119
120     def _write_legacy_cffi(self, buf):
121         """Send a binary-packed message to VPP."""
122         if not self.connected:
123             raise VppTransportShmemIOError(1, 'Not connected')
124         return vpp_api.vac_write(bytes(buf), len(buf))
125
126     def read(self, timeout=None):
127         if not self.connected:
128             raise VppTransportShmemIOError(1, 'Not connected')
129         if timeout is None:
130             timeout = self.read_timeout
131         mem = ffi.new("char **")
132         size = ffi.new("int *")
133         rv = vpp_api.vac_read(mem, size, timeout)
134         if rv:
135             strerror = 'vac_read failed.  It is likely that VPP died.'
136             raise VppTransportShmemIOError(rv, strerror)
137         msg = bytes(ffi.buffer(mem[0], size[0]))
138         vpp_api.vac_free(mem[0])
139         return msg