Python API: Change from cPython to CFFI.
Change-Id: I03e52466fb3f909ae52b8fba601168f3eadbd972
Signed-off-by: Ole Troan <ot@cisco.com>
diff --git a/src/vpp-api/python/Makefile.am b/src/vpp-api/python/Makefile.am
index 5407682..6f5beb6 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/pneum/pneum.c b/src/vpp-api/python/pneum/pneum.c
deleted file mode 100644
index da9d69d..0000000
--- a/src/vpp-api/python/pneum/pneum.c
+++ /dev/null
@@ -1,467 +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 <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <vnet/vnet.h>
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-
-#include <vpp/api/vpe_msg_enum.h>
-
-#include "pneum.h"
-
-/*
- * Asynchronous mode:
- * Client registers a callback. All messages are sent to the callback.
- * Synchronous mode:
- * Client calls blocking read().
- * Clients are expected to collate events on a queue.
- * pneum_write() -> suspends RX thread
- * pneum_read() -> resumes RX thread
- */
-
-#define vl_typedefs /* define message structures */
-#include <vpp/api/vpe_all_api_h.h>
-#undef vl_typedefs
-
-#define vl_endianfun /* define message structures */
-#include <vpp/api/vpe_all_api_h.h>
-#undef vl_endianfun
-
-vlib_main_t vlib_global_main;
-vlib_main_t **vlib_mains;
-
-typedef struct {
- u8 connected_to_vlib;
- pthread_t rx_thread_handle;
- pthread_t timeout_thread_handle;
- pthread_mutex_t queue_lock;
- pthread_cond_t suspend_cv;
- pthread_cond_t resume_cv;
- pthread_mutex_t timeout_lock;
- pthread_cond_t timeout_cv;
- pthread_cond_t timeout_cancel_cv;
- pthread_cond_t terminate_cv;
-} pneum_main_t;
-
-pneum_main_t pneum_main;
-pneum_callback_t pneum_callback;
-u16 read_timeout = 0;
-bool rx_is_running = false;
-
-static void
-init (void)
-{
- pneum_main_t *pm = &pneum_main;
- memset(pm, 0, sizeof(*pm));
- pthread_mutex_init(&pm->queue_lock, NULL);
- pthread_cond_init(&pm->suspend_cv, NULL);
- pthread_cond_init(&pm->resume_cv, NULL);
- pthread_mutex_init(&pm->timeout_lock, NULL);
- pthread_cond_init(&pm->timeout_cv, NULL);
- pthread_cond_init(&pm->timeout_cancel_cv, NULL);
- pthread_cond_init(&pm->terminate_cv, NULL);
-}
-
-static void
-cleanup (void)
-{
- pneum_main_t *pm = &pneum_main;
- pthread_cond_destroy(&pm->suspend_cv);
- pthread_cond_destroy(&pm->resume_cv);
- pthread_cond_destroy(&pm->timeout_cv);
- pthread_cond_destroy(&pm->timeout_cancel_cv);
- pthread_cond_destroy(&pm->terminate_cv);
- pthread_mutex_destroy(&pm->queue_lock);
- pthread_mutex_destroy(&pm->timeout_lock);
- memset (pm, 0, sizeof (*pm));
-}
-
-/*
- * Satisfy external references when -lvlib is not available.
- */
-void vlib_cli_output (struct vlib_main_t * vm, char * fmt, ...)
-{
- clib_warning ("vlib_cli_output called...");
-}
-
-void
-pneum_free (void * msg)
-{
- vl_msg_api_free (msg);
-}
-
-static void
-pneum_api_handler (void *msg)
-{
- u16 id = ntohs(*((u16 *)msg));
- msgbuf_t *msgbuf = (msgbuf_t *)(((u8 *)msg) - offsetof(msgbuf_t, data));
- int l = ntohl(msgbuf->data_len);
- if (l == 0)
- clib_warning("Message ID %d has wrong length: %d\n", id, l);
-
- /* Call Python callback */
- ASSERT(pneum_callback);
- (pneum_callback)(msg, l);
- pneum_free(msg);
-}
-
-static void *
-pneum_rx_thread_fn (void *arg)
-{
- unix_shared_memory_queue_t *q;
- pneum_main_t *pm = &pneum_main;
- api_main_t *am = &api_main;
- uword msg;
-
- q = am->vl_input_queue;
-
- while (1)
- while (!unix_shared_memory_queue_sub(q, (u8 *)&msg, 0))
- {
- u16 id = ntohs(*((u16 *)msg));
- switch (id) {
- case VL_API_RX_THREAD_EXIT:
- vl_msg_api_free((void *) msg);
- /* signal waiting threads that this thread is about to terminate */
- pthread_mutex_lock(&pm->queue_lock);
- pthread_cond_signal(&pm->terminate_cv);
- pthread_mutex_unlock(&pm->queue_lock);
- pthread_exit(0);
- return 0;
- break;
-
- case VL_API_MEMCLNT_RX_THREAD_SUSPEND:
- vl_msg_api_free((void * )msg);
- /* Suspend thread and signal reader */
- pthread_mutex_lock(&pm->queue_lock);
- pthread_cond_signal(&pm->suspend_cv);
- /* Wait for the resume signal */
- pthread_cond_wait (&pm->resume_cv, &pm->queue_lock);
- pthread_mutex_unlock(&pm->queue_lock);
- break;
-
- case VL_API_MEMCLNT_READ_TIMEOUT:
- clib_warning("Received read timeout in async thread\n");
- vl_msg_api_free((void *) msg);
- break;
-
- default:
- pneum_api_handler((void *)msg);
- }
- }
-}
-
-static void *
-pneum_timeout_thread_fn (void *arg)
-{
- vl_api_memclnt_read_timeout_t *ep;
- pneum_main_t *pm = &pneum_main;
- api_main_t *am = &api_main;
- struct timespec ts;
- struct timeval tv;
- u16 timeout;
- int rv;
-
- while (1)
- {
- /* Wait for poke */
- pthread_mutex_lock(&pm->timeout_lock);
- pthread_cond_wait (&pm->timeout_cv, &pm->timeout_lock);
- timeout = read_timeout;
- gettimeofday(&tv, NULL);
- ts.tv_sec = tv.tv_sec + timeout;
- ts.tv_nsec = 0;
- rv = pthread_cond_timedwait (&pm->timeout_cancel_cv,
- &pm->timeout_lock, &ts);
- pthread_mutex_unlock(&pm->timeout_lock);
- if (rv == ETIMEDOUT)
- {
- ep = vl_msg_api_alloc (sizeof (*ep));
- ep->_vl_msg_id = ntohs(VL_API_MEMCLNT_READ_TIMEOUT);
- vl_msg_api_send_shmem(am->vl_input_queue, (u8 *)&ep);
- }
- }
- pthread_exit(0);
-}
-
-void
-pneum_rx_suspend (void)
-{
- api_main_t *am = &api_main;
- pneum_main_t *pm = &pneum_main;
- vl_api_memclnt_rx_thread_suspend_t *ep;
-
- if (!pm->rx_thread_handle) return;
- pthread_mutex_lock(&pm->queue_lock);
- if (rx_is_running)
- {
- ep = vl_msg_api_alloc (sizeof (*ep));
- ep->_vl_msg_id = ntohs(VL_API_MEMCLNT_RX_THREAD_SUSPEND);
- vl_msg_api_send_shmem(am->vl_input_queue, (u8 *)&ep);
- /* Wait for RX thread to tell us it has suspendend */
- pthread_cond_wait(&pm->suspend_cv, &pm->queue_lock);
- rx_is_running = false;
- }
- pthread_mutex_unlock(&pm->queue_lock);
-}
-
-void
-pneum_rx_resume (void)
-{
- pneum_main_t *pm = &pneum_main;
- if (!pm->rx_thread_handle) return;
- pthread_mutex_lock(&pm->queue_lock);
- if (rx_is_running) return;
- pthread_cond_signal(&pm->resume_cv);
- rx_is_running = true;
- pthread_mutex_unlock(&pm->queue_lock);
-}
-
-uword *
-pneum_msg_table_get_hash (void)
-{
- api_main_t *am = &api_main;
- return (am->msg_index_by_name_and_crc);
-}
-
-int
-pneum_msg_table_size(void)
-{
- api_main_t *am = &api_main;
- return hash_elts(am->msg_index_by_name_and_crc);
-}
-
-int
-pneum_connect (char * name, char * chroot_prefix, pneum_callback_t cb,
- int rx_qlen)
-{
- int rv = 0;
- pneum_main_t *pm = &pneum_main;
-
- init();
- if (chroot_prefix != NULL)
- vl_set_memory_root_path (chroot_prefix);
-
- if ((rv = vl_client_api_map("/vpe-api"))) {
- clib_warning ("vl_client_api map rv %d", rv);
- return rv;
- }
-
- if (vl_client_connect(name, 0, rx_qlen) < 0) {
- vl_client_api_unmap();
- return (-1);
- }
-
- if (cb) {
- /* Start the rx queue thread */
- rv = pthread_create(&pm->rx_thread_handle, NULL, pneum_rx_thread_fn, 0);
- if (rv) {
- clib_warning("pthread_create returned %d", rv);
- vl_client_api_unmap();
- return (-1);
- }
- pneum_callback = cb;
- rx_is_running = true;
- }
-
- /* Start read timeout thread */
- rv = pthread_create(&pm->timeout_thread_handle, NULL,
- pneum_timeout_thread_fn, 0);
- if (rv) {
- clib_warning("pthread_create returned %d", rv);
- vl_client_api_unmap();
- return (-1);
- }
-
- pm->connected_to_vlib = 1;
-
- return (0);
-}
-
-int
-pneum_disconnect (void)
-{
- api_main_t *am = &api_main;
- pneum_main_t *pm = &pneum_main;
-
- if (!pm->connected_to_vlib) return 0;
-
- if (pm->rx_thread_handle) {
- vl_api_rx_thread_exit_t *ep;
- uword junk;
- ep = vl_msg_api_alloc (sizeof (*ep));
- ep->_vl_msg_id = ntohs(VL_API_RX_THREAD_EXIT);
- vl_msg_api_send_shmem(am->vl_input_queue, (u8 *)&ep);
-
- /* wait (with timeout) until RX thread has finished */
- struct timespec ts;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- ts.tv_sec = tv.tv_sec + 5;
- ts.tv_nsec = 0;
- pthread_mutex_lock(&pm->queue_lock);
- int rv = pthread_cond_timedwait(&pm->terminate_cv, &pm->queue_lock, &ts);
- pthread_mutex_unlock(&pm->queue_lock);
- /* now join so we wait until thread has -really- finished */
- if (rv == ETIMEDOUT)
- pthread_cancel(pm->rx_thread_handle);
- else
- pthread_join(pm->rx_thread_handle, (void **) &junk);
- }
- if (pm->timeout_thread_handle)
- pthread_cancel(pm->timeout_thread_handle);
-
- vl_client_disconnect();
- vl_client_api_unmap();
- pneum_callback = 0;
-
- cleanup();
-
- return (0);
-}
-
-static void
-set_timeout (unsigned short timeout)
-{
- pneum_main_t *pm = &pneum_main;
- pthread_mutex_lock(&pm->timeout_lock);
- read_timeout = timeout;
- pthread_cond_signal(&pm->timeout_cv);
- pthread_mutex_unlock(&pm->timeout_lock);
-}
-
-static void
-unset_timeout (void)
-{
- pneum_main_t *pm = &pneum_main;
- pthread_mutex_lock(&pm->timeout_lock);
- pthread_cond_signal(&pm->timeout_cancel_cv);
- pthread_mutex_unlock(&pm->timeout_lock);
-}
-
-int
-pneum_read (char **p, int *l, u16 timeout)
-{
- unix_shared_memory_queue_t *q;
- api_main_t *am = &api_main;
- pneum_main_t *pm = &pneum_main;
- uword msg;
- msgbuf_t *msgbuf;
-
- if (!pm->connected_to_vlib) return -1;
-
- *l = 0;
-
- if (am->our_pid == 0) return (-1);
-
- /* Poke timeout thread */
- if (timeout)
- set_timeout(timeout);
-
- q = am->vl_input_queue;
- int rv = unix_shared_memory_queue_sub(q, (u8 *)&msg, 0);
- if (rv == 0) {
- u16 msg_id = ntohs(*((u16 *)msg));
- switch (msg_id) {
- case VL_API_RX_THREAD_EXIT:
- printf("Received thread exit\n");
- return -1;
- case VL_API_MEMCLNT_RX_THREAD_SUSPEND:
- printf("Received thread suspend\n");
- goto error;
- case VL_API_MEMCLNT_READ_TIMEOUT:
- printf("Received read timeout %ds\n", timeout);
- goto error;
-
- default:
- msgbuf = (msgbuf_t *)(((u8 *)msg) - offsetof(msgbuf_t, data));
- *l = ntohl(msgbuf->data_len);
- if (*l == 0) {
- printf("Unregistered API message: %d\n", msg_id);
- goto error;
- }
- }
- *p = (char *)msg;
-
- /* Let timeout notification thread know we're done */
- unset_timeout();
-
- } else {
- printf("Read failed with %d\n", rv);
- }
- return (rv);
-
- error:
- vl_msg_api_free((void *) msg);
- /* Client might forget to resume RX thread on failure */
- pneum_rx_resume ();
- return -1;
-}
-
-/*
- * XXX: Makes the assumption that client_index is the first member
- */
-typedef VL_API_PACKED(struct _vl_api_header {
- u16 _vl_msg_id;
- u32 client_index;
-}) vl_api_header_t;
-
-static unsigned int
-pneum_client_index (void)
-{
- return (api_main.my_client_index);
-}
-
-int
-pneum_write (char *p, int l)
-{
- int rv = -1;
- api_main_t *am = &api_main;
- vl_api_header_t *mp = vl_msg_api_alloc(l);
- unix_shared_memory_queue_t *q;
- pneum_main_t *pm = &pneum_main;
-
- if (!pm->connected_to_vlib) return -1;
- if (!mp) return (-1);
-
- memcpy(mp, p, l);
- mp->client_index = pneum_client_index();
- q = am->shmem_hdr->vl_input_queue;
- rv = unix_shared_memory_queue_add(q, (u8 *)&mp, 0);
- if (rv != 0) {
- clib_warning("vpe_api_write fails: %d\n", rv);
- /* Clear message */
- pneum_free(mp);
- }
- return (rv);
-}
-
-uint32_t
-pneum_get_msg_index (unsigned char * name)
-{
- return vl_api_get_msg_index (name);
-}
diff --git a/src/vpp-api/python/pneum/pneum.h b/src/vpp-api/python/pneum/pneum.h
deleted file mode 100644
index c4b55ae..0000000
--- a/src/vpp-api/python/pneum/pneum.h
+++ /dev/null
@@ -1,34 +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.
- */
-#ifndef included_pneum_h
-#define included_pneum_h
-
-#include <stdint.h>
-#include <vppinfra/types.h>
-
-typedef void (*pneum_callback_t)(unsigned char * data, int len);
-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_msg_table_size(void);
-uint32_t pneum_get_msg_index(unsigned char * name);
-void pneum_rx_suspend (void);
-void pneum_rx_resume (void);
-
-#endif
diff --git a/src/vpp-api/python/pneum/test_pneum.c b/src/vpp-api/python/pneum/test_pneum.c
deleted file mode 100644
index 334e58e..0000000
--- a/src/vpp-api/python/pneum/test_pneum.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *------------------------------------------------------------------
- * test_pneum.c
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <time.h> /* time_t, time (for timestamp in second) */
-#include <sys/timeb.h> /* ftime, timeb (for timestamp in millisecond) */
-#include <sys/time.h> /* gettimeofday, timeval (for timestamp in microsecond) */
-
-#include <vnet/vnet.h>
-#include <vlib/vlib.h>
-#include <vlib/unix/unix.h>
-#include <vlibapi/api.h>
-#include <vlibmemory/api.h>
-#include <vnet/ip/ip.h>
-
-#include <vpp/api/vpe_msg_enum.h>
-#include <signal.h>
-#include <setjmp.h>
-#include "pneum.h"
-
-#define vl_typedefs /* define message structures */
-#include <vpp/api/vpe_all_api_h.h>
-#undef vl_typedefs
-
-/* we are not linking with vlib */
-vlib_main_t vlib_global_main;
-vlib_main_t **vlib_mains;
-
-volatile int sigterm_received = 0;
-volatile u32 result_ready;
-volatile u16 result_msg_id;
-
-/* M_NOALLOC: construct, but don't yet send a message */
-
-#define M_NOALLOC(T,t) \
- do { \
- result_ready = 0; \
- memset (mp, 0, sizeof (*mp)); \
- mp->_vl_msg_id = ntohs (VL_API_##T); \
- mp->client_index = am->my_client_index; \
- } while(0);
-
-
-
-int
-wrap_pneum_callback (char *data, int len)
-{
- //printf("Callback %d\n", len);
- result_ready = 1;
- result_msg_id = ntohs(*((u16 *)data));
- return (0);
-}
-
-int main (int argc, char ** argv)
-{
- api_main_t * am = &api_main;
- vl_api_show_version_t message;
- vl_api_show_version_t *mp;
- int async = 1;
- int rv = pneum_connect("pneum_client", NULL, NULL, 32 /* rx queue-length*/);
-
- if (rv != 0) {
- printf("Connect failed: %d\n", rv);
- exit(rv);
- }
-
- struct timeb timer_msec;
- long long int timestamp_msec_start; /* timestamp in millisecond. */
- if (!ftime(&timer_msec)) {
- timestamp_msec_start = ((long long int) timer_msec.time) * 1000ll +
- (long long int) timer_msec.millitm;
- }
- else {
- timestamp_msec_start = -1;
- }
-
-
- /*
- * Test vpe_api_write and vpe_api_read to send and recv message for an
- * API
- */
- int i;
- long int no_msgs = 10000;
- mp = &message;
-
- for (i = 0; i < no_msgs; i++) {
- /* Construct the API message */
- M_NOALLOC(SHOW_VERSION, show_version);
- pneum_write((char *)mp, sizeof(*mp));
-#ifndef __COVERITY__
- /* As given, async is always 1. Shut up Coverity about it */
- if (!async)
- while (result_ready == 0);
-#endif
- }
- if (async) {
- vl_api_control_ping_t control;
- vl_api_control_ping_t *mp;
- mp = &control;
- M_NOALLOC(CONTROL_PING, control_ping);
- pneum_write((char *)mp, sizeof(*mp));
-
- while (result_msg_id != VL_API_CONTROL_PING_REPLY);
- }
-
- long long int timestamp_msec_end; /* timestamp in millisecond. */
- if (!ftime(&timer_msec)) {
- timestamp_msec_end = ((long long int) timer_msec.time) * 1000ll +
- (long long int) timer_msec.millitm;
- }
- else {
- timestamp_msec_end = -1;
- }
-
- printf("Took %lld msec, %lld msgs/msec \n", (timestamp_msec_end - timestamp_msec_start),
- no_msgs/(timestamp_msec_end - timestamp_msec_start));
- fformat(stdout, "Exiting...\n");
- pneum_disconnect();
- exit (0);
-}
diff --git a/src/vpp-api/python/setup.cfg b/src/vpp-api/python/setup.cfg
index d645be7..79bc678 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 8a34d50..28c2ecc 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 0c40f17..81f6903 100644
--- a/src/vpp-api/python/vpp_papi/vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi.py
@@ -18,19 +18,47 @@
import sys, os, logging, collections, struct, json, threading, glob
import atexit, Queue
-logging.basicConfig(level=logging.DEBUG)
-import vpp_api
+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);
-def eprint(*args, **kwargs):
- """Print critical diagnostics to stderr."""
- print(*args, file=sys.stderr, **kwargs)
+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 @@
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 @@
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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 6688ffb..0000000
--- 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 c5a7eea..0000000
--- 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 <Python.h>
-#include "../pneum/pneum.h"
-#include <vppinfra/hash.h>
-
-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
-}