From 3cc4971882235a539bc6177e8e4b4d92129b3a12 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Wed, 8 Mar 2017 12:02:24 +0100 Subject: [PATCH] Python API: Change from cPython to CFFI. Change-Id: I03e52466fb3f909ae52b8fba601168f3eadbd972 Signed-off-by: Ole Troan --- Makefile | 5 +- build-root/rpm/vpp.spec | 12 +- src/Makefile.am | 1 + src/configure.ac | 9 + src/vpp-api.am | 45 +++++ src/vpp-api/{python => }/pneum/pneum.c | 25 ++- src/vpp-api/{python => }/pneum/pneum.h | 9 +- src/vpp-api/{python => }/pneum/test_pneum.c | 0 src/vpp-api/python/Makefile.am | 57 +------ src/vpp-api/python/setup.cfg | 2 +- src/vpp-api/python/setup.py | 15 +- src/vpp-api/python/{vpp_papi => }/vpp_papi.py | 124 +++++++++----- src/vpp-api/python/vpp_papi/__init__.py | 3 - src/vpp-api/python/vpp_papi/pneum_wrap.c | 226 -------------------------- test/Makefile | 2 +- test/vpp_papi_provider.py | 2 +- 16 files changed, 186 insertions(+), 351 deletions(-) create mode 100644 src/vpp-api.am rename src/vpp-api/{python => }/pneum/pneum.c (97%) rename src/vpp-api/{python => }/pneum/pneum.h (84%) rename src/vpp-api/{python => }/pneum/test_pneum.c (100%) rename src/vpp-api/python/{vpp_papi => }/vpp_papi.py (85%) delete mode 100644 src/vpp-api/python/vpp_papi/__init__.py delete mode 100644 src/vpp-api/python/vpp_papi/pneum_wrap.c diff --git a/Makefile b/Makefile index ebf94db724e..f47c98a54e1 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,8 @@ endif DEB_DEPENDS = curl build-essential autoconf automake bison libssl-dev ccache DEB_DEPENDS += debhelper dkms git libtool libganglia1-dev libapr1-dev dh-systemd DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config -DEB_DEPENDS += python-dev python-virtualenv python-pip lcov chrpath autoconf nasm +DEB_DEPENDS += lcov chrpath autoconf nasm +DEB_DEPENDS += python-dev python-virtualenv python-pip libffi6 ifeq ($(OS_VERSION_ID),14.04) DEB_DEPENDS += openjdk-8-jdk-headless else @@ -49,7 +50,7 @@ endif RPM_DEPENDS_GROUPS = 'Development Tools' RPM_DEPENDS = redhat-lsb glibc-static java-1.8.0-openjdk-devel yum-utils RPM_DEPENDS += openssl-devel https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm apr-devel -RPM_DEPENDS += python-devel python-virtualenv lcov chrpath +RPM_DEPENDS += python-devel python-virtualenv lcov chrpath libffi-devel RPM_DEPENDS += https://kojipkgs.fedoraproject.org//packages/nasm/2.12.02/2.fc26/x86_64/nasm-2.12.02-2.fc26.x86_64.rpm EPEL_DEPENDS = libconfuse-devel ganglia-devel diff --git a/build-root/rpm/vpp.spec b/build-root/rpm/vpp.spec index 7bc18ca4e8f..7fa97888f54 100644 --- a/build-root/rpm/vpp.spec +++ b/build-root/rpm/vpp.spec @@ -162,8 +162,8 @@ do done # Python bindings -mkdir -p -m755 %{buildroot}%{python2_sitelib} -install -p -m 666 %{_mu_build_dir}/%{_vpp_install_dir}/*/lib/python2.7/site-packages/vpp_papi-*.egg %{buildroot}%{python2_sitelib} +cd %{_mu_build_dir}/../src/vpp-api/python +%py2_install # # devel @@ -226,15 +226,9 @@ done sysctl --system %systemd_post vpp.service -%post api-python -easy_install -z %{python2_sitelib}/vpp_papi-*.egg - %preun %systemd_preun vpp.service -%preun api-python -easy_install -mxNq vpp_papi - %postun %systemd_postun @@ -285,7 +279,7 @@ fi %files api-python %defattr(644,root,root) -%{python2_sitelib}/vpp_papi-*.egg +%{python2_sitelib}/* %files devel %defattr(-,bin,bin) diff --git a/src/Makefile.am b/src/Makefile.am index 5daaa48e9df..41076e0ec7e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,6 +72,7 @@ include uri.am SUBDIRS += plugins if ENABLE_PAPI +include vpp-api.am SUBDIRS += vpp-api/python endif diff --git a/src/configure.ac b/src/configure.ac index d90740d9fef..5e02adc1160 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -201,6 +201,15 @@ AM_COND_IF([ENABLE_JAPI], AC_SUBST(JAR) ]) +############################################################################### +# PYTHON +############################################################################### + +AM_COND_IF([ENABLE_PAPI], +[ + AM_PATH_PYTHON +]) + ############################################################################### # Output ############################################################################### diff --git a/src/vpp-api.am b/src/vpp-api.am new file mode 100644 index 00000000000..0e05d60dddd --- /dev/null +++ b/src/vpp-api.am @@ -0,0 +1,45 @@ +# Copyright (c) 2017 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# VPP API C wrapper extension +# +lib_LTLIBRARIES += libpneum.la +libpneum_la_SOURCES = vpp-api/pneum/pneum.c +libpneum_la_LIBADD = \ + $(top_builddir)/libvppinfra.la \ + $(top_builddir)/libvlibmemoryclient.la \ + $(top_builddir)/libvlibapi.la \ + $(top_builddir)/libsvm.la \ + -lpthread -lm -lrt + +libpneum_la_LDFLAGS = -module +libpneum_la_CPPFLAGS = + +nobase_include_HEADERS += vpp-api/pneum/pneum.h + +# +# Test client +# +if ENABLE_TESTS +noinst_PROGRAMS += test_pneum +test_pneum_SOURCES = vpp-api/pneum/pneum.c vpp-api/pneum/test_pneum.c +test_pneum_LDADD = \ + $(top_builddir)/libvppinfra.la \ + $(top_builddir)/libvlibmemoryclient.la \ + $(top_builddir)/libvlibapi.la \ + $(top_builddir)/libsvm.la \ + -lpthread -lm -lrt +endif + +# vi:syntax=automake diff --git a/src/vpp-api/python/pneum/pneum.c b/src/vpp-api/pneum/pneum.c similarity index 97% rename from src/vpp-api/python/pneum/pneum.c rename to src/vpp-api/pneum/pneum.c index da9d69dfd4f..cbae5cff578 100644 --- a/src/vpp-api/python/pneum/pneum.c +++ b/src/vpp-api/pneum/pneum.c @@ -241,7 +241,7 @@ pneum_rx_resume (void) pthread_mutex_unlock(&pm->queue_lock); } -uword * +static uword * pneum_msg_table_get_hash (void) { api_main_t *am = &api_main; @@ -460,8 +460,29 @@ pneum_write (char *p, int l) return (rv); } -uint32_t +int pneum_get_msg_index (unsigned char * name) { return vl_api_get_msg_index (name); } + +int +pneum_msg_table_max_index(void) +{ + int max = 0; + hash_pair_t *hp; + uword *h = pneum_msg_table_get_hash(); + hash_foreach_pair (hp, h, + ({ + if (hp->value[0] > max) + max = hp->value[0]; + })); + + return max; +} + +void +pneum_set_error_handler (pneum_error_callback_t cb) +{ + if (cb) clib_error_register_handler (cb, 0); +} diff --git a/src/vpp-api/python/pneum/pneum.h b/src/vpp-api/pneum/pneum.h similarity index 84% rename from src/vpp-api/python/pneum/pneum.h rename to src/vpp-api/pneum/pneum.h index c4b55ae0be1..669298df838 100644 --- a/src/vpp-api/python/pneum/pneum.h +++ b/src/vpp-api/pneum/pneum.h @@ -19,16 +19,19 @@ #include typedef void (*pneum_callback_t)(unsigned char * data, int len); +typedef void (*pneum_error_callback_t)(void *, unsigned char *, int); int pneum_connect(char * name, char * chroot_prefix, pneum_callback_t cb, int rx_qlen); int pneum_disconnect(void); int pneum_read(char **data, int *l, unsigned short timeout); int pneum_write(char *data, int len); void pneum_free(void * msg); -uword * pneum_msg_table_get_hash (void); + +int pneum_get_msg_index(unsigned char * name); int pneum_msg_table_size(void); -uint32_t pneum_get_msg_index(unsigned char * name); +int pneum_msg_table_max_index(void); + void pneum_rx_suspend (void); void pneum_rx_resume (void); - +void pneum_set_error_handler(pneum_error_callback_t); #endif diff --git a/src/vpp-api/python/pneum/test_pneum.c b/src/vpp-api/pneum/test_pneum.c similarity index 100% rename from src/vpp-api/python/pneum/test_pneum.c rename to src/vpp-api/pneum/test_pneum.c diff --git a/src/vpp-api/python/Makefile.am b/src/vpp-api/python/Makefile.am index 540768229c5..6f5beb69b17 100644 --- a/src/vpp-api/python/Makefile.am +++ b/src/vpp-api/python/Makefile.am @@ -11,52 +11,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -AUTOMAKE_OPTIONS = foreign -ACLOCAL_AMFLAGS = -I m4 -AM_LIBTOOLFLAGS = --quiet -AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} - -BUILT_SOURCES = -bin_PROGRAMS = -CLEANFILES = -lib_LTLIBRARIES = -noinst_PROGRAMS = -nobase_include_HEADERS = pneum/pneum.h - -# -# Python / C extension -# -lib_LTLIBRARIES += libpneum.la -libpneum_la_SOURCES = pneum/pneum.c -libpneum_la_LIBADD = \ - $(top_builddir)/libvppinfra.la \ - $(top_builddir)/libvlibmemoryclient.la \ - $(top_builddir)/libvlibapi.la \ - $(top_builddir)/libsvm.la \ - -lpthread -lm -lrt - -libpneum_la_LDFLAGS = -module -libpneum_la_CPPFLAGS = - -# TODO: Support both Python 2 and 3. -install-exec-local: $(lib_LTLIBRARIES) - cd $(srcdir); \ - mkdir -p $(pythondir); \ - mkdir -p $(pyexecdir); \ - PYTHONUSERBASE=$(prefix) \ - python setup.py build_ext -L $(libdir) \ - -I $(prefix)/include/ install --user - -# -# Test client -# -if ENABLE_TESTS -noinst_PROGRAMS += test_pneum -test_pneum_SOURCES = pneum/pneum.c pneum/test_pneum.c -test_pneum_LDADD = \ - $(top_builddir)/libvppinfra.la \ - $(top_builddir)/libvlibmemoryclient.la \ - $(top_builddir)/libvlibapi.la \ - $(top_builddir)/libsvm.la \ - -lpthread -lm -lrt -endif +install-exec-local: + (cd $(srcdir) ; $(PYTHON) $(srcdir)/setup.py build \ + --build-base $(shell readlink -f $(builddir))/build \ + install \ + --root / \ + --prefix $(DESTDIR)$(prefix) \ + --single-version-externally-managed \ + --verbose) diff --git a/src/vpp-api/python/setup.cfg b/src/vpp-api/python/setup.cfg index d645be77604..79bc67848ff 100644 --- a/src/vpp-api/python/setup.cfg +++ b/src/vpp-api/python/setup.cfg @@ -2,4 +2,4 @@ # This flag says that the code is written to work on both Python 2 and Python # 3. If at all possible, it is good practice to do this. If you cannot, you # will need to generate wheels for each Python version that you support. -universal=0 +universal=1 diff --git a/src/vpp-api/python/setup.py b/src/vpp-api/python/setup.py index 8a34d501e77..28c2ecce1ab 100644 --- a/src/vpp-api/python/setup.py +++ b/src/vpp-api/python/setup.py @@ -13,23 +13,18 @@ # limitations under the License. try: - from setuptools import setup, Extension + from setuptools import setup except ImportError: - from distutils.core import setup, Extension + from distutils.core import setup setup (name = 'vpp_papi', - version = '1.3', + version = '1.4', description = 'VPP Python binding', author = 'Ole Troan', author_email = 'ot@cisco.com', test_suite = 'tests', - packages=['vpp_papi'], - ext_modules = [ - Extension( - 'vpp_api', - sources = ['vpp_papi/pneum_wrap.c'], - libraries = ['pneum'], - )], + install_requires=['cffi'], + py_modules=['vpp_papi'], long_description = '''VPP Python language binding.''', zip_safe = True, ) diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi.py similarity index 85% rename from src/vpp-api/python/vpp_papi/vpp_papi.py rename to src/vpp-api/python/vpp_papi.py index 0c40f1710e5..81f6903be5a 100644 --- a/src/vpp-api/python/vpp_papi/vpp_papi.py +++ b/src/vpp-api/python/vpp_papi.py @@ -18,19 +18,47 @@ from __future__ import print_function import sys, os, logging, collections, struct, json, threading, glob import atexit, Queue -logging.basicConfig(level=logging.DEBUG) -import vpp_api - -def eprint(*args, **kwargs): - """Print critical diagnostics to stderr.""" - print(*args, file=sys.stderr, **kwargs) +from cffi import FFI +ffi = FFI() +ffi.cdef(""" +typedef void (*pneum_callback_t)(unsigned char * data, int len); +typedef void (*pneum_error_callback_t)(void *, unsigned char *, int); +int pneum_connect(char * name, char * chroot_prefix, pneum_callback_t cb, + int rx_qlen); +int pneum_disconnect(void); +int pneum_read(char **data, int *l, unsigned short timeout); +int pneum_write(char *data, int len); +void pneum_free(void * msg); + +int pneum_get_msg_index(unsigned char * name); +int pneum_msg_table_size(void); +int pneum_msg_table_max_index(void); + +void pneum_rx_suspend (void); +void pneum_rx_resume (void); +void pneum_set_error_handler(pneum_error_callback_t); + """) + +# Barfs on failure, no need to check success. +vpp_api = ffi.dlopen('libpneum.so') def vpp_atexit(self): """Clean up VPP connection on shutdown.""" if self.connected: - eprint ('Cleaning up VPP on exit') + self.logger.debug('Cleaning up VPP on exit') self.disconnect() +vpp_object = None + +@ffi.callback("void(unsigned char *, int)") +def pneum_callback_sync(data, len): + vpp_object.msg_handler_sync(ffi.buffer(data, len)) +@ffi.callback("void(unsigned char *, int)") +def pneum_callback_async(data, len): + vpp_object.msg_handler_async(ffi.buffer(data, len)) +@ffi.callback("void(void *, unsigned char *, int)") +def pneum_error_handler(arg, msg, msg_len): + vpp_object.logger.warning("PNEUM: %s", ffi.string(msg, msg_len)) class Empty(object): pass @@ -57,7 +85,8 @@ class VPP(): provides a means to register a callback function to receive these messages in a background thread. """ - def __init__(self, apifiles = None, testmode = False, async_thread = True): + def __init__(self, apifiles = None, testmode = False, async_thread = True, + logger = logging.getLogger('vpp_papi'), loglevel = 'debug'): """Create a VPP API object. apifiles is a list of files containing API @@ -66,6 +95,11 @@ class VPP(): provided this will load the API files from VPP's default install location. """ + global vpp_object + vpp_object = self + self.logger = logger + logging.basicConfig(level=getattr(logging, loglevel.upper())) + self.messages = {} self.id_names = [] self.id_msgdef = [] @@ -103,6 +137,9 @@ class VPP(): # Make sure we allow VPP to clean up the message rings. atexit.register(vpp_atexit, self) + # Register error handler + vpp_api.pneum_set_error_handler(pneum_error_handler) + class ContextId(object): """Thread-safe provider of unique context IDs.""" def __init__(self): @@ -285,7 +322,7 @@ class VPP(): return self.messages[name]['return_tuple'] return None - def add_message(self, name, msgdef): + def add_message(self, name, msgdef, typeonly = False): if name in self.messages: raise ValueError('Duplicate message name: ' + name) @@ -311,10 +348,11 @@ class VPP(): self.messages[name] = msg self.messages[name]['args'] = args self.messages[name]['argtypes'] = argtypes + self.messages[name]['typeonly'] = typeonly return self.messages[name] def add_type(self, name, typedef): - return self.add_message('vl_api_' + name + '_t', typedef) + return self.add_message('vl_api_' + name + '_t', typedef, typeonly=True) def make_function(self, name, i, msgdef, multipart, async): if (async): @@ -338,12 +376,11 @@ class VPP(): self.id_msgdef = [None] * (self.vpp_dictionary_maxid + 1) self._api = Empty() for name, msgdef in self.messages.iteritems(): - if name in self.vpp_dictionary: - if self.messages[name]['crc'] != self.vpp_dictionary[name]['crc']: - raise ValueError(3, 'Failed CRC checksum ' + name + - ' ' + self.messages[name]['crc'] + - ' ' + self.vpp_dictionary[name]['crc']) - i = self.vpp_dictionary[name]['id'] + if self.messages[name]['typeonly']: continue + crc = self.messages[name]['crc'] + n = name + '_' + crc[2:] + i = vpp_api.pneum_get_msg_index(bytes(n)) + if i > 0: self.id_msgdef[i] = msgdef self.id_names[i] = name multipart = True if name.find('_dump') > 0 else False @@ -356,46 +393,45 @@ class VPP(): 3, "Conflicting name in JSON definition: `%s'" % name) setattr(self, name, f) # old API stuff ends here + else: + self.logger.debug('No such message type or failed CRC checksum: %s', n) def _write (self, buf): """Send a binary-packed message to VPP.""" if not self.connected: raise IOError(1, 'Not connected') - return vpp_api.write(str(buf)) + return vpp_api.pneum_write(str(buf), len(buf)) def _read (self): if not self.connected: raise IOError(1, 'Not connected') - - return vpp_api.read(self.read_timeout) - - def _load_dictionary(self): - self.vpp_dictionary = {} - self.vpp_dictionary_maxid = 0 - d = vpp_api.msg_table() - - if not d: - raise IOError(3, 'Cannot get VPP API dictionary') - for i,n in d: - name, crc = n.rsplit('_', 1) - crc = '0x' + crc - self.vpp_dictionary[name] = { 'id' : i, 'crc' : crc } - self.vpp_dictionary_maxid = max(self.vpp_dictionary_maxid, i) + mem = ffi.new("char **") + size = ffi.new("int *") + rv = vpp_api.pneum_read(mem, size, self.read_timeout) + if rv: + raise IOError(rv, 'pneum_read filed') + msg = bytes(ffi.buffer(mem[0], size[0])) + vpp_api.pneum_free(mem[0]) + return msg def connect_internal(self, name, msg_handler, chroot_prefix, rx_qlen, async): - rv = vpp_api.connect(name, msg_handler, chroot_prefix, rx_qlen) + rv = vpp_api.pneum_connect(name, chroot_prefix, msg_handler, rx_qlen) if rv != 0: raise IOError(2, 'Connect failed') self.connected = True - self._load_dictionary() + self.vpp_dictionary_maxid = vpp_api.pneum_msg_table_max_index() self._register_functions(async=async) # Initialise control ping - self.control_ping_index = self.vpp_dictionary['control_ping']['id'] + crc = self.messages['control_ping']['crc'] + self.control_ping_index = \ + vpp_api.pneum_get_msg_index( + bytes('control_ping' + '_' + crc[2:])) self.control_ping_msgdef = self.messages['control_ping'] - def connect(self, name, chroot_prefix = None, async = False, rx_qlen = 32): + def connect(self, name, chroot_prefix = ffi.NULL, + async = False, rx_qlen = 32): """Attach to VPP. name - the name of the client. @@ -404,12 +440,12 @@ class VPP(): rx_qlen - the length of the VPP message receive queue between client and server. """ - msg_handler = self.msg_handler_sync if not async \ - else self.msg_handler_async + msg_handler = pneum_callback_sync if not async \ + else pneum_callback_async return self.connect_internal(name, msg_handler, chroot_prefix, rx_qlen, async) - def connect_sync (self, name, chroot_prefix = None, rx_qlen = 32): + def connect_sync (self, name, chroot_prefix = ffi.NULL, rx_qlen = 32): """Attach to VPP in synchronous mode. Application must poll for events. name - the name of the client. @@ -418,12 +454,12 @@ class VPP(): client and server. """ - return self.connect_internal(name, None, chroot_prefix, rx_qlen, + return self.connect_internal(name, ffi.NULL, chroot_prefix, rx_qlen, async=False) def disconnect(self): """Detach from VPP.""" - rv = vpp_api.disconnect() + rv = vpp_api.pneum_disconnect() self.connected = False return rv @@ -452,7 +488,7 @@ class VPP(): def decode_incoming_msg(self, msg): if not msg: - eprint('vpp_api.read failed') + self.logger.warning('vpp_api.read failed') return i, ci = self.header.unpack_from(msg, 0) @@ -514,7 +550,7 @@ class VPP(): kwargs['_vl_msg_id'] = i b = self.encode(msgdef, kwargs) - vpp_api.suspend() + vpp_api.pneum_rx_suspend() self._write(b) if multipart: @@ -544,7 +580,7 @@ class VPP(): rl.append(r) - vpp_api.resume() + vpp_api.pneum_rx_resume() return rl diff --git a/src/vpp-api/python/vpp_papi/__init__.py b/src/vpp-api/python/vpp_papi/__init__.py deleted file mode 100644 index 6688ffb8ced..00000000000 --- a/src/vpp-api/python/vpp_papi/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__) -from . vpp_papi import * - diff --git a/src/vpp-api/python/vpp_papi/pneum_wrap.c b/src/vpp-api/python/vpp_papi/pneum_wrap.c deleted file mode 100644 index c5a7eea1e00..00000000000 --- a/src/vpp-api/python/vpp_papi/pneum_wrap.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "../pneum/pneum.h" -#include - -static PyObject *pneum_callback = NULL; - -static void -wrap_pneum_callback (unsigned char * data, int len) -{ - PyGILState_STATE gstate; - PyObject *result;//, *arglist; - - gstate = PyGILState_Ensure(); - - /* Time to call the callback */ -#if PY_VERSION_HEX >= 0x03000000 - result = PyObject_CallFunction(pneum_callback, "y#", data, len); -#else - result = PyObject_CallFunction(pneum_callback, "s#", data, len); -#endif - if (result) - Py_DECREF(result); - else - PyErr_Print(); - - PyGILState_Release(gstate); -} - -static PyObject * -wrap_connect (PyObject *self, PyObject *args, PyObject *kw) -{ - char * name, * chroot_prefix = NULL; - int rx_qlen = 32; /* default rx queue length */ - int rv; - PyObject * temp = NULL; - pneum_callback_t cb = NULL; - - if (!PyArg_ParseTuple(args, "sOzi:wrap_connect", - &name, &temp, &chroot_prefix, &rx_qlen)) - return (NULL); - - if (temp != Py_None) - { - if (!PyCallable_Check(temp)) - { - PyErr_SetString(PyExc_TypeError, "parameter must be callable"); - return NULL; - } - - Py_XINCREF(temp); /* Add a reference to new callback */ - Py_XDECREF(pneum_callback); /* Dispose of previous callback */ - pneum_callback = temp; /* Remember new callback */ - cb = wrap_pneum_callback; - } - Py_BEGIN_ALLOW_THREADS - rv = pneum_connect(name, chroot_prefix, cb, rx_qlen); - Py_END_ALLOW_THREADS - return PyLong_FromLong(rv); -} - -static PyObject * -wrap_disconnect (PyObject *self, PyObject *args) -{ - int rv; - Py_BEGIN_ALLOW_THREADS - rv = pneum_disconnect(); - Py_END_ALLOW_THREADS - return PyLong_FromLong(rv); -} - -static PyObject * -wrap_write (PyObject *self, PyObject *args) -{ - char *data; - int len, rv; - - if (!PyArg_ParseTuple(args, "s#", &data, &len)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - rv = pneum_write(data, len); - Py_END_ALLOW_THREADS - - return PyLong_FromLong(rv); -} - -static PyObject * -wrap_read (PyObject *self, PyObject *args) -{ - char *data; - int len, rv; - unsigned short timeout; - - if (!PyArg_ParseTuple(args, "H", &timeout)) - return (NULL); - Py_BEGIN_ALLOW_THREADS - rv = pneum_read(&data, &len, timeout); - Py_END_ALLOW_THREADS - - if (rv != 0) { Py_RETURN_NONE; } -#if PY_VERSION_HEX >= 0x03000000 - PyObject *ret = Py_BuildValue("y#", data, len); -#else - PyObject *ret = Py_BuildValue("s#", data, len); -#endif - pneum_free(data); - if (!ret) { Py_RETURN_NONE; } - - return ret; -} - -static PyObject * -wrap_msg_table (PyObject *self, PyObject *args) -{ - int i = 0, rv = 0; - hash_pair_t *hp; - uword *h = pneum_msg_table_get_hash(); - PyObject *ret = PyList_New(pneum_msg_table_size()); - if (!ret) goto error; - hash_foreach_pair (hp, h, - ({ - PyObject *item = PyTuple_New(2); - if (!item) goto error; - rv = PyTuple_SetItem(item, 0, PyLong_FromLong((u32)hp->value[0])); - if (rv) goto error; - rv = PyTuple_SetItem(item, 1, PyString_FromString((char *)hp->key)); - if (rv) goto error; - PyList_SetItem(ret, i, item); - i++; - })); - - return ret; - - error: - /* TODO: Raise exception */ - printf("msg_table failed"); - Py_RETURN_NONE; -} - -static PyObject * -wrap_suspend (PyObject *self, PyObject *args) -{ - Py_BEGIN_ALLOW_THREADS - pneum_rx_suspend(); - Py_END_ALLOW_THREADS - Py_RETURN_NONE; -} - -static PyObject * -wrap_resume (PyObject *self, PyObject *args) -{ - Py_BEGIN_ALLOW_THREADS - pneum_rx_resume(); - Py_END_ALLOW_THREADS - Py_RETURN_NONE; -} - -static PyMethodDef vpp_api_Methods[] = { - {"connect", wrap_connect, METH_VARARGS, "Connect to the VPP API."}, - {"disconnect", wrap_disconnect, METH_VARARGS, "Disconnect from the VPP API."}, - {"write", wrap_write, METH_VARARGS, "Write data to the VPP API."}, - {"read", wrap_read, METH_VARARGS, "Read data from the VPP API."}, - {"msg_table", wrap_msg_table, METH_VARARGS, "Get API dictionary."}, - {"suspend", wrap_suspend, METH_VARARGS, "Suspend RX thread."}, - {"resume", wrap_resume, METH_VARARGS, "Resume RX thread."}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -#if PY_VERSION_HEX >= 0x03000000 -PyMODINIT_FUNC -PyInit_vpp_api (void) -#else -void -initvpp_api (void) -#endif -{ -#if PY_VERSION_HEX >= 0x03000000 - static struct PyModuleDef vpp_api_module = { -#if PY_VERSION_HEX >= 0x03020000 - PyModuleDef_HEAD_INIT, -#else - { - PyObject_HEAD_INIT(NULL) - NULL, /* m_init */ - 0, /* m_index */ - NULL, /* m_copy */ - }, -#endif - (char *) "vpp_api", - NULL, - -1, - vpp_api_Methods, - NULL, - NULL, - NULL, - NULL - }; -#endif - - /* Ensure threading is initialised */ - if (!PyEval_ThreadsInitialized()) { - PyEval_InitThreads(); - } - -#if PY_VERSION_HEX >= 0x03000000 - return PyModule_Create(&vpp_api_module); -#else - Py_InitModule((char *) "vpp_api", vpp_api_Methods); - return; -#endif -} diff --git a/test/Makefile b/test/Makefile index 8594633ba8e..4338e09675e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -30,7 +30,7 @@ UNITTEST_EXTRA_OPTS="-f" endif PYTHON_VENV_PATH=$(VPP_PYTHON_PREFIX)/virtualenv -PYTHON_DEPENDS=scapy==2.3.3 pexpect subprocess32 git+https://github.com/klement/py-lispnetworking@setup +PYTHON_DEPENDS=scapy==2.3.3 pexpect subprocess32 cffi git+https://github.com/klement/py-lispnetworking@setup SCAPY_SOURCE=$(PYTHON_VENV_PATH)/lib/python2.7/site-packages/ BUILD_COV_DIR = $(BR)/test-cov diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 2d683dc2500..7f9e2ae1293 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -55,7 +55,7 @@ class VppPapiProvider(object): for filename in fnmatch.filter(filenames, '*.api.json'): jsonfiles.append(os.path.join(root, filename)) - self.vpp = VPP(jsonfiles) + self.vpp = VPP(jsonfiles, logger=test_class.logger) self._events = deque() def __enter__(self): -- 2.16.6