Python API: Change from cPython to CFFI.
Change-Id: I03e52466fb3f909ae52b8fba601168f3eadbd972 Signed-off-by: Ole Troan <ot@cisco.com>
This commit is contained in:
5
Makefile
5
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
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -72,6 +72,7 @@ include uri.am
|
||||
SUBDIRS += plugins
|
||||
|
||||
if ENABLE_PAPI
|
||||
include vpp-api.am
|
||||
SUBDIRS += vpp-api/python
|
||||
endif
|
||||
|
||||
|
@@ -201,6 +201,15 @@ AM_COND_IF([ENABLE_JAPI],
|
||||
AC_SUBST(JAR)
|
||||
])
|
||||
|
||||
###############################################################################
|
||||
# PYTHON
|
||||
###############################################################################
|
||||
|
||||
AM_COND_IF([ENABLE_PAPI],
|
||||
[
|
||||
AM_PATH_PYTHON
|
||||
])
|
||||
|
||||
###############################################################################
|
||||
# Output
|
||||
###############################################################################
|
||||
|
45
src/vpp-api.am
Normal file
45
src/vpp-api.am
Normal file
@@ -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
|
@@ -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);
|
||||
}
|
@@ -19,16 +19,19 @@
|
||||
#include <vppinfra/types.h>
|
||||
|
||||
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
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
)
|
||||
|
@@ -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
|
||||
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 @@ 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
|
||||
|
@@ -1,3 +0,0 @@
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
from . vpp_papi import *
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
|
||||
|
@@ -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):
|
||||
|
Reference in New Issue
Block a user