X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvpp-api%2Fpython%2Fvpp_papi.py;h=e7e1707c435b82390cd4a9f78b908dff2e9ff2b0;hb=a74b7419bd6c7c5b23f59253b0b0b6c0d683794d;hp=c1fa9e8bff937b489c7eacfee6150b9c204dd049;hpb=4df97165159b3b115b31eb1cad55782ac97e3c7e;p=vpp.git diff --git a/src/vpp-api/python/vpp_papi.py b/src/vpp-api/python/vpp_papi.py index c1fa9e8bff9..e7e1707c435 100644 --- a/src/vpp-api/python/vpp_papi.py +++ b/src/vpp-api/python/vpp_papi.py @@ -25,6 +25,7 @@ import threading import glob import atexit from cffi import FFI +import cffi if sys.version[0] == '2': import Queue as queue @@ -54,7 +55,6 @@ void vac_set_error_handler(vac_error_callback_t); # Barfs on failure, no need to check success. vpp_api = ffi.dlopen('libvppapiclient.so') - def vpp_atexit(self): """Clean up VPP connection on shutdown.""" if self.connected: @@ -129,7 +129,6 @@ class VPP(): self.messages = {} self.id_names = [] self.id_msgdef = [] - self.buffersize = 10000 self.connected = False self.header = struct.Struct('>HI') self.apifiles = [] @@ -167,6 +166,14 @@ class VPP(): # Register error handler vpp_api.vac_set_error_handler(vac_error_handler) + # Support legacy CFFI + # from_buffer supported from 1.8.0 + (major, minor, patch) = [int(s) for s in cffi.__version__.split('.', 3)] + if major >= 1 and minor >= 8: + self._write = self._write_new_cffi + else: + self._write = self._write_legacy_cffi + class ContextId(object): """Thread-safe provider of unique context IDs.""" def __init__(self): @@ -199,35 +206,45 @@ class VPP(): if not vl: if e > 0 and t == 'u8': # Fixed byte array - return struct.Struct('>' + str(e) + 's') + s = struct.Struct('>' + str(e) + 's') + return s.size, s if e > 0: # Fixed array of base type - return [e, struct.Struct('>' + base_types[t])] + s = struct.Struct('>' + base_types[t]) + return s.size, [e, s] elif e == 0: # Old style variable array - return [-1, struct.Struct('>' + base_types[t])] + s = struct.Struct('>' + base_types[t]) + return s.size, [-1, s] else: # Variable length array - return [vl, struct.Struct('>s')] if t == 'u8' else \ - [vl, struct.Struct('>' + base_types[t])] + if t == 'u8': + s = struct.Struct('>s') + return s.size, [vl, s] + else: + s = struct.Struct('>' + base_types[t]) + return s.size, [vl, s] - return struct.Struct('>' + base_types[t]) + s = struct.Struct('>' + base_types[t]) + return s.size, s if t in self.messages: + size = self.messages[t]['sizes'][0] + # Return a list in case of array if e > 0 and not vl: - return [e, lambda self, encode, buf, offset, args: ( + return size, [e, lambda self, encode, buf, offset, args: ( self.__struct_type(encode, self.messages[t], buf, offset, args))] if vl: - return [vl, lambda self, encode, buf, offset, args: ( + return size, [vl, lambda self, encode, buf, offset, args: ( self.__struct_type(encode, self.messages[t], buf, offset, args))] elif e == 0: # Old style VLA raise NotImplementedError(1, 'No support for compound types ' + t) - return lambda self, encode, buf, offset, args: ( + return size, lambda self, encode, buf, offset, args: ( self.__struct_type(encode, self.messages[t], buf, offset, args) ) @@ -246,7 +263,9 @@ class VPP(): for k in kwargs: if k not in msgdef['args']: - raise ValueError(1, 'Invalid field-name in message call ' + k) + raise ValueError(1,'Non existing argument [' + k + ']' + \ + ' used in call to: ' + \ + self.id_names[kwargs['_vl_msg_id']] + '()' ) for k, v in vpp_iterator(msgdef['args']): off += size @@ -254,6 +273,8 @@ class VPP(): if type(v) is list: if callable(v[1]): e = kwargs[v[0]] if v[0] in kwargs else v[0] + if e != len(kwargs[k]): + raise (ValueError(1, 'Input list length mismatch: %s (%s != %s)' % (k, e, len(kwargs[k])))) size = 0 for i in range(e): size += v[1](self, True, buf, off + size, @@ -261,6 +282,8 @@ class VPP(): else: if v[0] in kwargs: l = kwargs[v[0]] + if l != len(kwargs[k]): + raise ValueError(1, 'Input list length mistmatch: %s (%s != %s)' % (k, l, len(kwargs[k]))) else: l = len(kwargs[k]) if v[1].size == 1: @@ -275,6 +298,8 @@ class VPP(): if callable(v): size = v(self, True, buf, off, kwargs[k]) else: + if type(kwargs[k]) is str and v.size < len(kwargs[k]): + raise ValueError(1, 'Input list length mistmatch: %s (%s < %s)' % (k, v.size, len(kwargs[k]))) v.pack_into(buf, off, kwargs[k]) size = v.size else: @@ -287,9 +312,17 @@ class VPP(): return self.messages[name] return None + def get_size(self, sizes, kwargs): + total_size = sizes[0] + for e in sizes[1]: + if e in kwargs and type(kwargs[e]) is list: + total_size += len(kwargs[e]) * sizes[1][e] + return total_size + def encode(self, msgdef, kwargs): # Make suitably large buffer - buf = bytearray(self.buffersize) + size = self.get_size(msgdef['sizes'], kwargs) + buf = bytearray(size) offset = 0 size = self.__struct_type(True, msgdef, buf, offset, kwargs) return buf[:offset + size] @@ -335,6 +368,7 @@ class VPP(): size += v[1].size else: if callable(v): + size = 0 (s, l) = v(self, False, buf, off, None) res.append(l) size += s @@ -357,6 +391,8 @@ class VPP(): argtypes = collections.OrderedDict() fields = [] msg = {} + total_size = 0 + sizes = {} for i, f in enumerate(msgdef): if type(f) is dict and 'crc' in f: msg['crc'] = f['crc'] @@ -365,7 +401,18 @@ class VPP(): field_name = f[1] if len(f) == 3 and f[2] == 0 and i != len(msgdef) - 2: raise ValueError('Variable Length Array must be last: ' + name) - args[field_name] = self.__struct(*f) + size, s = self.__struct(*f) + args[field_name] = s + if type(s) == list and type(s[0]) == int and type(s[1]) == struct.Struct: + if s[0] < 0: + sizes[field_name] = size + else: + sizes[field_name] = size + total_size += s[0] * size + else: + sizes[field_name] = size + total_size += size + argtypes[field_name] = field_type if len(f) == 4: # Find offset to # elements field idx = list(args.keys()).index(f[3]) - i @@ -377,6 +424,7 @@ class VPP(): self.messages[name]['args'] = args self.messages[name]['argtypes'] = argtypes self.messages[name]['typeonly'] = typeonly + self.messages[name]['sizes'] = [total_size, sizes] return self.messages[name] def add_type(self, name, typedef): @@ -429,12 +477,18 @@ class VPP(): self.logger.debug( 'No such message type or failed CRC checksum: %s', n) - def _write(self, buf): + def _write_new_cffi(self, buf): """Send a binary-packed message to VPP.""" if not self.connected: raise IOError(1, 'Not connected') return vpp_api.vac_write(ffi.from_buffer(buf), len(buf)) + def _write_legacy_cffi(self, buf): + """Send a binary-packed message to VPP.""" + if not self.connected: + raise IOError(1, 'Not connected') + return vpp_api.vac_write(str(buf), len(buf)) + def _read(self): if not self.connected: raise IOError(1, 'Not connected') @@ -442,15 +496,15 @@ class VPP(): size = ffi.new("int *") rv = vpp_api.vac_read(mem, size, self.read_timeout) if rv: - raise IOError(rv, 'vac_read filed') + raise IOError(rv, 'vac_read failed') msg = bytes(ffi.buffer(mem[0], size[0])) vpp_api.vac_free(mem[0]) return msg def connect_internal(self, name, msg_handler, chroot_prefix, rx_qlen, async): - rv = vpp_api.vac_connect(name.encode(), chroot_prefix.encode(), - msg_handler, rx_qlen) + pfx = chroot_prefix.encode() if chroot_prefix else ffi.NULL + rv = vpp_api.vac_connect(name.encode(), pfx, msg_handler, rx_qlen) if rv != 0: raise IOError(2, 'Connect failed') self.connected = True @@ -465,7 +519,7 @@ class VPP(): self.control_ping_msgdef = self.messages['control_ping'] return rv - def connect(self, name, chroot_prefix=ffi.NULL, async=False, rx_qlen=32): + def connect(self, name, chroot_prefix=None, async=False, rx_qlen=32): """Attach to VPP. name - the name of the client. @@ -478,7 +532,7 @@ class VPP(): return self.connect_internal(name, msg_handler, chroot_prefix, rx_qlen, async) - def connect_sync(self, name, chroot_prefix=ffi.NULL, rx_qlen=32): + def connect_sync(self, name, chroot_prefix=None, rx_qlen=32): """Attach to VPP in synchronous mode. Application must poll for events. name - the name of the client.