Add C++ API
Change-Id: Iff634f22d43470e2dc028387b3816257fd7b4156 Signed-off-by: Klement Sekera <ksekera@cisco.com>
This commit is contained in:

committed by
Neale Ranns

parent
9d063047eb
commit
dc15be2ca7
38
.clang-format
Normal file
38
.clang-format
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBraces: GNU
|
||||
ColumnLimit: 79
|
||||
IndentCaseLabels: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 60
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerBindsToType: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: Always
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInParentheses: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpaceAfterControlStatementKeyword: true
|
||||
Cpp11BracedListStyle: true
|
||||
Standard: Cpp11
|
||||
SortIncludes: false
|
||||
IndentWidth: 2
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
ContinuationIndentWidth: 4
|
||||
...
|
@ -32,37 +32,75 @@ fi
|
||||
# don't *fail*.
|
||||
command -v indent > /dev/null
|
||||
if [ $? != 0 ]; then
|
||||
echo "Cound not find required commend \"indent\". Checkstyle aborted"
|
||||
echo "Cound not find required command \"indent\". Checkstyle aborted"
|
||||
exit ${EXIT_CODE}
|
||||
fi
|
||||
indent --version
|
||||
|
||||
# Check to make sure we have clang-format. Exit if we don't with an error message, but
|
||||
# don't *fail*.
|
||||
command -v clang-format > /dev/null
|
||||
if [ $? != 0 ]; then
|
||||
echo "Could not find command \"clang-format\". Checking C++ files will cause abort"
|
||||
HAVE_CLANG_FORMAT=0
|
||||
else
|
||||
HAVE_CLANG_FORMAT=1
|
||||
clang-format --version
|
||||
fi
|
||||
|
||||
cd ${VPP_DIR}
|
||||
git status
|
||||
for i in ${FILELIST}; do
|
||||
if [ -f ${i} ] && [ ${i} != "build-root/scripts/checkstyle.sh" ] && [ ${i} != "extras/emacs/fix-coding-style.el" ]; then
|
||||
grep -q "fd.io coding-style-patch-verification: ON" ${i}
|
||||
if [ $? == 0 ]; then
|
||||
EXTENSION=`basename ${i} | sed 's/^\w\+.//'`
|
||||
case ${EXTENSION} in
|
||||
hpp|cpp|cc|hh)
|
||||
CMD="clang-format"
|
||||
if [ ${HAVE_CLANG_FORMAT} == 0 ]; then
|
||||
echo "C++ file detected. Abort. (missing clang-format)"
|
||||
exit ${EXIT_CODE}
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
CMD="indent"
|
||||
;;
|
||||
esac
|
||||
CHECKSTYLED_FILES="${CHECKSTYLED_FILES} ${i}"
|
||||
if [ ${FIX} == 0 ]; then
|
||||
indent ${i} -o ${i}.out1 > /dev/null 2>&1
|
||||
indent ${i}.out1 -o ${i}.out2 > /dev/null 2>&1
|
||||
# Remove trailing whitespace
|
||||
sed -i -e 's/[[:space:]]*$//' ${i}.out2
|
||||
if [ "${CMD}" == "clang-format" ]
|
||||
then
|
||||
clang-format ${i} > ${i}.out2
|
||||
else
|
||||
indent ${i} -o ${i}.out1 > /dev/null 2>&1
|
||||
indent ${i}.out1 -o ${i}.out2 > /dev/null 2>&1
|
||||
fi
|
||||
# Remove trailing whitespace
|
||||
sed -i -e 's/[[:space:]]*$//' ${i}.out2
|
||||
diff -q ${i} ${i}.out2
|
||||
else
|
||||
indent ${i}
|
||||
indent ${i}
|
||||
# Remove trailing whitespace
|
||||
sed -i -e 's/[[:space:]]*$//' ${i}
|
||||
if [ "${CMD}" == "clang-format" ]; then
|
||||
clang-format -i ${i} > /dev/null 2>&1
|
||||
else
|
||||
indent ${i}
|
||||
indent ${i}
|
||||
fi
|
||||
# Remove trailing whitespace
|
||||
sed -i -e 's/[[:space:]]*$//' ${i}
|
||||
fi
|
||||
if [ $? != 0 ]; then
|
||||
EXIT_CODE=1
|
||||
echo
|
||||
echo "Checkstyle failed for ${i}."
|
||||
echo "Run indent (twice!) as shown to fix the problem:"
|
||||
echo "indent ${VPP_DIR}${i}"
|
||||
echo "indent ${VPP_DIR}${i}"
|
||||
if [ "${CMD}" == "clang-format" ]; then
|
||||
echo "Run clang-format as shown to fix the problem:"
|
||||
echo "clang-format -i ${VPP_DIR}${i}"
|
||||
else
|
||||
echo "Run indent (twice!) as shown to fix the problem:"
|
||||
echo "indent ${VPP_DIR}${i}"
|
||||
echo "indent ${VPP_DIR}${i}"
|
||||
fi
|
||||
fi
|
||||
if [ -f ${i}.out1 ]; then
|
||||
rm ${i}.out1
|
||||
|
@ -7,6 +7,7 @@ AC_CONFIG_FILES([Makefile plugins/Makefile vpp-api/python/Makefile vpp-api/java/
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AM_PROG_AS
|
||||
AM_PROG_LIBTOOL
|
||||
AC_PROG_YACC
|
||||
|
@ -15,7 +15,7 @@ AUTOMAKE_OPTIONS = foreign
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
AM_LIBTOOLFLAGS = --quiet
|
||||
|
||||
AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} -I. -I$(top_srcdir)/vpp-api/vapi
|
||||
AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} -I. -I$(top_srcdir)/vpp-api/
|
||||
|
||||
AM_LDFLAGS = -shared -avoid-version -rpath /none -no-undefined
|
||||
|
||||
@ -23,26 +23,33 @@ bin_PROGRAMS =
|
||||
noinst_LTLIBRARIES =
|
||||
CLEANDIRS =
|
||||
|
||||
%.api.vapi.h: %.api.json vapi_c_gen.py
|
||||
vapi/%.api.vapi.h: %.api.json vapi_c_gen.py vapi_json_parser.py
|
||||
@echo " VAPI C GEN $< " $@ ; \
|
||||
mkdir -p `dirname $@` ; \
|
||||
$(top_srcdir)/vpp-api/vapi/vapi_c_gen.py $<
|
||||
$(top_srcdir)/vpp-api/vapi/vapi_c_gen.py --prefix=vapi $<
|
||||
|
||||
vapi/%.api.vapi.hpp: %.api.json vapi_cpp_gen.py vapi_c_gen.py vapi_json_parser.py
|
||||
@echo " VAPI CPP GEN $< " $@ ; \
|
||||
mkdir -p `dirname $@` ; \
|
||||
$(top_srcdir)/vpp-api/vapi/vapi_cpp_gen.py --prefix=vapi --gen-h-prefix=vapi $<
|
||||
|
||||
%.api.json:
|
||||
find $(top_builddir) -name '$@' | xargs ln -s
|
||||
|
||||
BUILT_SOURCES = $(shell find $(top_builddir) -name '*.api.json' | xargs -n1 basename) \
|
||||
$(patsubst %.api.json,%.api.vapi.h,$(JSON_FILES))
|
||||
$(patsubst %.api.json,vapi/%.api.vapi.h,$(JSON_FILES)) \
|
||||
$(patsubst %.api.json,vapi/%.api.vapi.hpp,$(JSON_FILES))
|
||||
|
||||
vapi.c: $(BUILT_SOURCES)
|
||||
|
||||
JSON_FILES = $(wildcard *.api.json)
|
||||
|
||||
|
||||
lib_LTLIBRARIES = libvapiclient.la
|
||||
|
||||
libvapiclient_la_SOURCES = vapi.c
|
||||
|
||||
libvapiclient_la_DEPENDENCIES = libvapiclient.map
|
||||
|
||||
libvapiclient_la_LIBADD = -lpthread -lm -lrt \
|
||||
$(top_builddir)/libvppinfra.la \
|
||||
$(top_builddir)/libvlibmemoryclient.la \
|
||||
@ -54,10 +61,14 @@ libvapiclient_la_LDFLAGS = \
|
||||
|
||||
libvapiclient_la_CPPFLAGS = -I. -I$(top_builddir)/vpp-api/vapi
|
||||
|
||||
nobase_include_HEADERS = ${top_srcdir}/vpp-api/client/vppapiclient.h \
|
||||
vapi.h \
|
||||
vapiincludedir = $(includedir)/vapi
|
||||
|
||||
vapiinclude_HEADERS = vapi.h \
|
||||
vapi.hpp \
|
||||
vapi_dbg.h \
|
||||
vapi_common.h \
|
||||
vapi_internal.h \
|
||||
$(patsubst %.api.json,%.api.vapi.h,$(JSON_FILES))
|
||||
$(patsubst %.api.json,vapi/%.api.vapi.h,$(JSON_FILES)) \
|
||||
$(patsubst %.api.json,vapi/%.api.vapi.hpp,$(JSON_FILES))
|
||||
|
||||
# vi:syntax=automake
|
||||
|
@ -23,6 +23,7 @@ VAPICLIENT_17.07 {
|
||||
vapi_register_msg;
|
||||
vapi_get_client_index;
|
||||
vapi_is_nonblocking;
|
||||
vapi_requests_empty;
|
||||
vapi_requests_full;
|
||||
vapi_gen_req_context;
|
||||
vapi_producer_lock;
|
||||
@ -36,6 +37,8 @@ VAPICLIENT_17.07 {
|
||||
vapi_get_context_offset;
|
||||
vapi_msg_id_control_ping;
|
||||
vapi_msg_id_control_ping_reply;
|
||||
vapi_get_message_count;
|
||||
vapi_get_msg_name;
|
||||
|
||||
local: *;
|
||||
};
|
||||
|
@ -102,7 +102,7 @@ vapi_requests_full (vapi_ctx_t ctx)
|
||||
return (ctx->requests_count == ctx->requests_size);
|
||||
}
|
||||
|
||||
static bool
|
||||
bool
|
||||
vapi_requests_empty (vapi_ctx_t ctx)
|
||||
{
|
||||
return (0 == ctx->requests_count);
|
||||
@ -229,6 +229,16 @@ vapi_msg_free (vapi_ctx_t ctx, void *msg)
|
||||
vl_msg_api_free (msg);
|
||||
}
|
||||
|
||||
vapi_msg_id_t
|
||||
vapi_lookup_vapi_msg_id_t (vapi_ctx_t ctx, u16 vl_msg_id)
|
||||
{
|
||||
if (vl_msg_id <= ctx->vl_msg_id_max)
|
||||
{
|
||||
return ctx->vl_msg_id_to_vapi_msg_t[vl_msg_id];
|
||||
}
|
||||
return ~0;
|
||||
}
|
||||
|
||||
vapi_error_e
|
||||
vapi_ctx_alloc (vapi_ctx_t * result)
|
||||
{
|
||||
@ -420,16 +430,17 @@ vapi_send (vapi_ctx_t ctx, void *msg)
|
||||
vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
|
||||
if (id < __vapi_metadata.count)
|
||||
{
|
||||
VAPI_DBG ("send msg %u[%s]", msgid, __vapi_metadata.msgs[id]->name);
|
||||
VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid,
|
||||
__vapi_metadata.msgs[id]->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
VAPI_DBG ("send msg %u[UNKNOWN]", msgid);
|
||||
VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VAPI_DBG ("send msg %u[UNKNOWN]", msgid);
|
||||
VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
|
||||
}
|
||||
#endif
|
||||
tmp = unix_shared_memory_queue_add (q, (u8 *) & msg,
|
||||
@ -522,7 +533,26 @@ vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size)
|
||||
}
|
||||
*msg = (u8 *) data;
|
||||
*msg_size = ntohl (msgbuf->data_len);
|
||||
VAPI_DBG ("recv msg %p", *msg);
|
||||
#if VAPI_DEBUG
|
||||
unsigned msgid = be16toh (*(u16 *) * msg);
|
||||
if (msgid <= ctx->vl_msg_id_max)
|
||||
{
|
||||
vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
|
||||
if (id < __vapi_metadata.count)
|
||||
{
|
||||
VAPI_DBG ("recv msg@%p:%u[%s]", *msg, msgid,
|
||||
__vapi_metadata.msgs[id]->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -534,7 +564,6 @@ vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size)
|
||||
vapi_error_e
|
||||
vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode)
|
||||
{
|
||||
/* FIXME */
|
||||
return VAPI_ENOTSUP;
|
||||
}
|
||||
|
||||
@ -657,7 +686,7 @@ vapi_dispatch_event (vapi_ctx_t ctx, vapi_msg_id_t id, void *msg)
|
||||
return VAPI_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
bool
|
||||
vapi_msg_is_with_context (vapi_msg_id_t id)
|
||||
{
|
||||
assert (id <= __vapi_metadata.count);
|
||||
@ -785,10 +814,6 @@ vapi_is_nonblocking (vapi_ctx_t ctx)
|
||||
return (VAPI_MODE_NONBLOCKING == ctx->mode);
|
||||
}
|
||||
|
||||
bool vapi_requests_full (vapi_ctx_t ctx);
|
||||
|
||||
size_t vapi_get_request_count (vapi_ctx_t ctx);
|
||||
|
||||
size_t
|
||||
vapi_get_max_request_count (vapi_ctx_t ctx)
|
||||
{
|
||||
@ -886,6 +911,18 @@ vapi_producer_unlock (vapi_ctx_t ctx)
|
||||
return VAPI_OK;
|
||||
}
|
||||
|
||||
size_t
|
||||
vapi_get_message_count ()
|
||||
{
|
||||
return __vapi_metadata.count;
|
||||
}
|
||||
|
||||
const char *
|
||||
vapi_get_msg_name (vapi_msg_id_t id)
|
||||
{
|
||||
return __vapi_metadata.msgs[id]->name;
|
||||
}
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
|
@ -21,6 +21,12 @@
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <vppinfra/types.h>
|
||||
#include <vapi/vapi_common.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file vapi.h
|
||||
@ -36,39 +42,7 @@
|
||||
* process). It's not recommended to mix the higher and lower level APIs. Due
|
||||
* to version issues, the higher-level APIs are not part of the shared library.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAPI_OK = 0, /**< success */
|
||||
VAPI_EINVAL, /**< invalid value encountered */
|
||||
VAPI_EAGAIN, /**< operation would block */
|
||||
VAPI_ENOTSUP, /**< operation not supported */
|
||||
VAPI_ENOMEM, /**< out of memory */
|
||||
VAPI_ENORESP, /**< no response to request */
|
||||
VAPI_EMAP_FAIL, /**< failure while mapping api */
|
||||
VAPI_ECON_FAIL, /**< failure while connecting to vpp */
|
||||
VAPI_EINCOMPATIBLE, /**< fundamental incompatibility while connecting to vpp
|
||||
(control ping/control ping reply mismatch) */
|
||||
VAPI_MUTEX_FAILURE, /**< failure manipulating internal mutex(es) */
|
||||
VAPI_EUSER, /**< user error used for breaking dispatch,
|
||||
never used by VAPI */
|
||||
} vapi_error_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAPI_MODE_BLOCKING = 1, /**< operations block until response received */
|
||||
VAPI_MODE_NONBLOCKING = 2, /**< operations never block */
|
||||
} vapi_mode_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAPI_WAIT_FOR_READ, /**< wait until a message can be read */
|
||||
VAPI_WAIT_FOR_WRITE, /**< wait until a message can be written */
|
||||
VAPI_WAIT_FOR_READ_WRITE, /**< wait until a read or write can be done */
|
||||
} vapi_wait_mode_e;
|
||||
|
||||
typedef int vapi_msg_id_t;
|
||||
typedef struct vapi_ctx_s *vapi_ctx_t;
|
||||
typedef struct vapi_ctx_s *vapi_ctx_t;
|
||||
|
||||
/**
|
||||
* @brief allocate vapi message of given size
|
||||
@ -80,7 +54,7 @@ typedef struct vapi_ctx_s *vapi_ctx_t;
|
||||
*
|
||||
* @return pointer to message or NULL if out of memory
|
||||
*/
|
||||
void *vapi_msg_alloc (vapi_ctx_t ctx, size_t size);
|
||||
void *vapi_msg_alloc (vapi_ctx_t ctx, size_t size);
|
||||
|
||||
/**
|
||||
* @brief free a vapi message
|
||||
@ -90,7 +64,7 @@ void *vapi_msg_alloc (vapi_ctx_t ctx, size_t size);
|
||||
* @param ctx opaque vapi context
|
||||
* @param msg message to be freed
|
||||
*/
|
||||
void vapi_msg_free (vapi_ctx_t ctx, void *msg);
|
||||
void vapi_msg_free (vapi_ctx_t ctx, void *msg);
|
||||
|
||||
/**
|
||||
* @brief allocate vapi context
|
||||
@ -99,18 +73,18 @@ void vapi_msg_free (vapi_ctx_t ctx, void *msg);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_ctx_alloc (vapi_ctx_t * result);
|
||||
vapi_error_e vapi_ctx_alloc (vapi_ctx_t * result);
|
||||
|
||||
/**
|
||||
* @brief free vapi context
|
||||
*/
|
||||
void vapi_ctx_free (vapi_ctx_t ctx);
|
||||
void vapi_ctx_free (vapi_ctx_t ctx);
|
||||
|
||||
/**
|
||||
* @brief check if message identified by it's message id is known by the vpp to
|
||||
* which the connection is open
|
||||
*/
|
||||
bool vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t type);
|
||||
bool vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t type);
|
||||
|
||||
/**
|
||||
* @brief connect to vpp
|
||||
@ -124,10 +98,10 @@ bool vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t type);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_connect (vapi_ctx_t ctx, const char *name,
|
||||
const char *chroot_prefix,
|
||||
int max_outstanding_requests,
|
||||
int response_queue_size, vapi_mode_e mode);
|
||||
vapi_error_e vapi_connect (vapi_ctx_t ctx, const char *name,
|
||||
const char *chroot_prefix,
|
||||
int max_outstanding_requests,
|
||||
int response_queue_size, vapi_mode_e mode);
|
||||
|
||||
/**
|
||||
* @brief disconnect from vpp
|
||||
@ -136,7 +110,7 @@ vapi_error_e vapi_connect (vapi_ctx_t ctx, const char *name,
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_disconnect (vapi_ctx_t ctx);
|
||||
vapi_error_e vapi_disconnect (vapi_ctx_t ctx);
|
||||
|
||||
/**
|
||||
* @brief get event file descriptor
|
||||
@ -149,7 +123,7 @@ vapi_error_e vapi_disconnect (vapi_ctx_t ctx);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_get_fd (vapi_ctx_t ctx, int *fd);
|
||||
vapi_error_e vapi_get_fd (vapi_ctx_t ctx, int *fd);
|
||||
|
||||
/**
|
||||
* @brief low-level api for sending messages to vpp
|
||||
@ -162,7 +136,7 @@ vapi_error_e vapi_get_fd (vapi_ctx_t ctx, int *fd);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
|
||||
vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
|
||||
|
||||
/**
|
||||
* @brief low-level api for atomically sending two messages to vpp - either
|
||||
@ -177,7 +151,7 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2);
|
||||
vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2);
|
||||
|
||||
/**
|
||||
* @brief low-level api for reading messages from vpp
|
||||
@ -191,7 +165,7 @@ vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size);
|
||||
vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size);
|
||||
|
||||
/**
|
||||
* @brief wait for connection to become readable or writable
|
||||
@ -201,14 +175,14 @@ vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode);
|
||||
vapi_error_e vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode);
|
||||
|
||||
/**
|
||||
* @brief pick next message sent by vpp and call the appropriate callback
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
|
||||
vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
|
||||
|
||||
/**
|
||||
* @brief loop vapi_dispatch_one until responses to all currently outstanding
|
||||
@ -224,11 +198,11 @@ vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
|
||||
*
|
||||
* @return VAPI_OK on success, other error code on error
|
||||
*/
|
||||
vapi_error_e vapi_dispatch (vapi_ctx_t ctx);
|
||||
vapi_error_e vapi_dispatch (vapi_ctx_t ctx);
|
||||
|
||||
/** generic vapi event callback */
|
||||
typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx,
|
||||
void *payload);
|
||||
typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* @brief set event callback to call when message with given id is dispatched
|
||||
@ -238,8 +212,8 @@ typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx,
|
||||
* @param callback callback
|
||||
* @param callback_ctx context pointer stored and passed to callback
|
||||
*/
|
||||
void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
|
||||
vapi_event_cb callback, void *callback_ctx);
|
||||
void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
|
||||
vapi_event_cb callback, void *callback_ctx);
|
||||
|
||||
/**
|
||||
* @brief clear event callback for given message id
|
||||
@ -247,12 +221,12 @@ void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
|
||||
* @param ctx opaque vapi context
|
||||
* @param id message id
|
||||
*/
|
||||
void vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id);
|
||||
void vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id);
|
||||
|
||||
/** generic vapi event callback */
|
||||
typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx,
|
||||
void *callback_ctx,
|
||||
vapi_msg_id_t id, void *msg);
|
||||
typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx,
|
||||
void *callback_ctx,
|
||||
vapi_msg_id_t id, void *msg);
|
||||
/**
|
||||
* @brief set generic event callback
|
||||
*
|
||||
@ -263,16 +237,20 @@ typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx,
|
||||
* @param callback callback
|
||||
* @param callback_ctx context pointer stored and passed to callback
|
||||
*/
|
||||
void vapi_set_generic_event_cb (vapi_ctx_t ctx,
|
||||
vapi_generic_event_cb callback,
|
||||
void *callback_ctx);
|
||||
void vapi_set_generic_event_cb (vapi_ctx_t ctx,
|
||||
vapi_generic_event_cb callback,
|
||||
void *callback_ctx);
|
||||
|
||||
/**
|
||||
* @brief clear generic event callback
|
||||
*
|
||||
* @param ctx opaque vapi context
|
||||
*/
|
||||
void vapi_clear_generic_event_cb (vapi_ctx_t ctx);
|
||||
void vapi_clear_generic_event_cb (vapi_ctx_t ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
905
src/vpp-api/vapi/vapi.hpp
Normal file
905
src/vpp-api/vapi/vapi.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@ class CField(Field):
|
||||
def get_swap_to_be_code(self, struct, var):
|
||||
if self.len is not None:
|
||||
if self.len > 0:
|
||||
return "do { int i; for (i = 0; i < %d; ++i) { %s } }"\
|
||||
return "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"\
|
||||
" while(0);" % (
|
||||
self.len,
|
||||
self.type.get_swap_to_be_code(struct, "%s[i]" % var))
|
||||
@ -38,7 +38,7 @@ class CField(Field):
|
||||
else:
|
||||
nelem_field = "%s%s" % (struct, self.nelem_field.name)
|
||||
return (
|
||||
"do { int i; for (i = 0; i < %s; ++i) { %s } }"
|
||||
"do { unsigned i; for (i = 0; i < %s; ++i) { %s } }"
|
||||
" while(0);" %
|
||||
(nelem_field, self.type.get_swap_to_be_code(
|
||||
struct, "%s[i]" % var)))
|
||||
@ -47,14 +47,14 @@ class CField(Field):
|
||||
def get_swap_to_host_code(self, struct, var):
|
||||
if self.len is not None:
|
||||
if self.len > 0:
|
||||
return "do { int i; for (i = 0; i < %d; ++i) { %s } }"\
|
||||
return "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"\
|
||||
" while(0);" % (
|
||||
self.len,
|
||||
self.type.get_swap_to_host_code(struct, "%s[i]" % var))
|
||||
else:
|
||||
# nelem_field already swapped to host here...
|
||||
return (
|
||||
"do { int i; for (i = 0; i < %s%s; ++i) { %s } }"
|
||||
"do { unsigned i; for (i = 0; i < %s%s; ++i) { %s } }"
|
||||
" while(0);" %
|
||||
(struct, self.nelem_field.name,
|
||||
self.type.get_swap_to_host_code(
|
||||
@ -199,14 +199,17 @@ class CMessage (Message):
|
||||
def get_alloc_func_name(self):
|
||||
return "vapi_alloc_%s" % self.name
|
||||
|
||||
def get_alloc_vla_param_names(self):
|
||||
return [self.get_alloc_func_vla_field_length_name(f)
|
||||
for f in self.fields
|
||||
if f.nelem_field is not None]
|
||||
|
||||
def get_alloc_func_decl(self):
|
||||
return "%s* %s(struct vapi_ctx_s *ctx%s)" % (
|
||||
self.get_c_name(),
|
||||
self.get_alloc_func_name(),
|
||||
"".join([", size_t %s" %
|
||||
self.get_alloc_func_vla_field_length_name(f)
|
||||
for f in self.fields
|
||||
if f.nelem_field is not None]))
|
||||
"".join([", size_t %s" % n for n in
|
||||
self.get_alloc_vla_param_names()]))
|
||||
|
||||
def get_alloc_func_def(self):
|
||||
extra = []
|
||||
@ -228,7 +231,8 @@ class CMessage (Message):
|
||||
for f in self.fields
|
||||
if f.nelem_field is not None
|
||||
])),
|
||||
" msg = vapi_msg_alloc(ctx, size);",
|
||||
" /* cast here required to play nicely with C++ world ... */",
|
||||
" msg = (%s*)vapi_msg_alloc(ctx, size);" % self.get_c_name(),
|
||||
" if (!msg) {",
|
||||
" return NULL;",
|
||||
" }",
|
||||
@ -441,7 +445,7 @@ class CMessage (Message):
|
||||
def get_event_cb_func_decl(self):
|
||||
if not self.is_reply():
|
||||
raise Exception(
|
||||
"Cannot register event callback for non-reply function")
|
||||
"Cannot register event callback for non-reply message")
|
||||
if self.has_payload():
|
||||
return "\n".join([
|
||||
"void vapi_set_%s_event_cb (" %
|
||||
@ -498,7 +502,7 @@ class CMessage (Message):
|
||||
' offsetof(%s, context),' % self.header.get_c_name()
|
||||
if has_context else ' 0,',
|
||||
(' offsetof(%s, payload),' % self.get_c_name())
|
||||
if self.has_payload() else '-1,',
|
||||
if self.has_payload() else ' ~0,',
|
||||
' sizeof(%s),' % self.get_c_name(),
|
||||
' (generic_swap_fn_t)%s,' % self.get_swap_to_be_func_name(),
|
||||
' (generic_swap_fn_t)%s,' % self.get_swap_to_host_func_name(),
|
||||
@ -529,8 +533,8 @@ vapi_send_with_control_ping (vapi_ctx_t ctx, void *msg, u32 context)
|
||||
"""
|
||||
|
||||
|
||||
def gen_json_header(parser, logger, j, io):
|
||||
logger.info("Generating header `%s'" % io.name)
|
||||
def gen_json_unified_header(parser, logger, j, io, name):
|
||||
logger.info("Generating header `%s'" % name)
|
||||
orig_stdout = sys.stdout
|
||||
sys.stdout = io
|
||||
include_guard = "__included_%s" % (
|
||||
@ -538,131 +542,22 @@ def gen_json_header(parser, logger, j, io):
|
||||
print("#ifndef %s" % include_guard)
|
||||
print("#define %s" % include_guard)
|
||||
print("")
|
||||
print("#include <vapi_internal.h>")
|
||||
print("#include <stdlib.h>")
|
||||
print("#include <stddef.h>")
|
||||
print("#include <arpa/inet.h>")
|
||||
print("#include <vapi/vapi_internal.h>")
|
||||
print("#include <vapi/vapi.h>")
|
||||
print("#include <vapi/vapi_dbg.h>")
|
||||
print("")
|
||||
if io.name == "vpe.api.vapi.h":
|
||||
print("static inline vapi_error_e vapi_send_with_control_ping "
|
||||
"(vapi_ctx_t ctx, void * msg, u32 context);")
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
|
||||
print("")
|
||||
for t in parser.types_by_json[j].values():
|
||||
try:
|
||||
print("%s" % t.get_c_def())
|
||||
print("")
|
||||
except:
|
||||
pass
|
||||
for t in parser.types_by_json[j].values():
|
||||
print("%s;" % t.get_swap_to_be_func_decl())
|
||||
print("")
|
||||
print("%s;" % t.get_swap_to_host_func_decl())
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
print("%s" % m.get_c_def())
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
if not m.is_reply():
|
||||
print("%s;" % m.get_alloc_func_decl())
|
||||
print("")
|
||||
print("%s;" % m.get_op_func_decl())
|
||||
if m.has_payload():
|
||||
print("%s;" % m.get_swap_payload_to_be_func_decl())
|
||||
print("")
|
||||
print("%s;" % m.get_swap_payload_to_host_func_decl())
|
||||
print("")
|
||||
print("%s;" % m.get_calc_msg_size_func_decl())
|
||||
print("")
|
||||
print("%s;" % m.get_swap_to_host_func_decl())
|
||||
print("")
|
||||
print("%s;" % m.get_swap_to_be_func_decl())
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
if not m.is_reply():
|
||||
continue
|
||||
print("%s;" % m.get_event_cb_func_decl())
|
||||
print("")
|
||||
|
||||
if io.name == "vpe.api.vapi.h":
|
||||
print("%s" % vapi_send_with_control_ping)
|
||||
print("")
|
||||
|
||||
print("#ifdef __cplusplus")
|
||||
print("extern \"C\" {")
|
||||
print("#endif")
|
||||
sys.stdout = orig_stdout
|
||||
|
||||
|
||||
def gen_json_code(parser, logger, j, io):
|
||||
logger.info("Generating code `%s'" % io.name)
|
||||
orig_stdout = sys.stdout
|
||||
sys.stdout = io
|
||||
print("#include <%s>" % json_to_header_name(j))
|
||||
print("#include <stdlib.h>")
|
||||
print("#include <stddef.h>")
|
||||
print("#include <arpa/inet.h>")
|
||||
print("#include <vapi_internal.h>")
|
||||
print("#include <vapi_dbg.h>")
|
||||
print("")
|
||||
for t in parser.types_by_json[j].values():
|
||||
print("%s" % t.get_swap_to_be_func_def())
|
||||
print("")
|
||||
print("%s" % t.get_swap_to_host_func_def())
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
if m.has_payload():
|
||||
print("%s" % m.get_swap_payload_to_be_func_def())
|
||||
print("")
|
||||
print("%s" % m.get_swap_payload_to_host_func_def())
|
||||
print("")
|
||||
print("%s" % m.get_calc_msg_size_func_def())
|
||||
print("")
|
||||
print("%s" % m.get_swap_to_be_func_def())
|
||||
print("")
|
||||
print("%s" % m.get_swap_to_host_func_def())
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
if m.is_reply():
|
||||
continue
|
||||
print("%s" % m.get_alloc_func_def())
|
||||
print("")
|
||||
print("%s" % m.get_op_func_def())
|
||||
print("")
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
print("%s" % m.get_c_constructor())
|
||||
print("")
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
if not m.is_reply():
|
||||
continue
|
||||
print("%s;" % m.get_event_cb_func_def())
|
||||
print("")
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
print("vapi_msg_id_t %s;" % m.get_msg_id_name())
|
||||
sys.stdout = orig_stdout
|
||||
|
||||
|
||||
def gen_json_unified_header(parser, logger, j, io):
|
||||
logger.info("Generating header `%s'" % io.name)
|
||||
orig_stdout = sys.stdout
|
||||
sys.stdout = io
|
||||
include_guard = "__included_%s" % (
|
||||
j.replace(".", "_").replace("/", "_").replace("-", "_"))
|
||||
print("#ifndef %s" % include_guard)
|
||||
print("#define %s" % include_guard)
|
||||
print("")
|
||||
print("#include <vapi_internal.h>")
|
||||
print("#include <vapi.h>")
|
||||
print("#include <stdlib.h>")
|
||||
print("#include <stddef.h>")
|
||||
print("#include <arpa/inet.h>")
|
||||
print("#include <vapi_dbg.h>")
|
||||
if io.name == "vpe.api.vapi.h":
|
||||
if name == "vpe.api.vapi.h":
|
||||
print("")
|
||||
print("static inline vapi_error_e vapi_send_with_control_ping "
|
||||
"(vapi_ctx_t ctx, void * msg, u32 context);")
|
||||
else:
|
||||
print("#include <vpe.api.vapi.h>")
|
||||
print("#include <vapi/vpe.api.vapi.h>")
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
|
||||
@ -725,46 +620,33 @@ def gen_json_unified_header(parser, logger, j, io):
|
||||
print("")
|
||||
print("")
|
||||
|
||||
if io.name == "vpe.api.vapi.h":
|
||||
if name == "vpe.api.vapi.h":
|
||||
print("%s" % vapi_send_with_control_ping)
|
||||
print("")
|
||||
|
||||
print("#ifdef __cplusplus")
|
||||
print("}")
|
||||
print("#endif")
|
||||
print("")
|
||||
print("#endif")
|
||||
sys.stdout = orig_stdout
|
||||
|
||||
|
||||
def json_to_header_name(json_name):
|
||||
def json_to_c_header_name(json_name):
|
||||
if json_name.endswith(".json"):
|
||||
return "%s.vapi.h" % os.path.splitext(json_name)[0]
|
||||
raise Exception("Unexpected json name `%s'!" % json_name)
|
||||
|
||||
|
||||
def json_to_code_name(json_name):
|
||||
if json_name.endswith(".json"):
|
||||
return "%s.vapi.c" % os.path.splitext(json_name)[0]
|
||||
raise Exception("Unexpected json name `%s'!" % json_name)
|
||||
|
||||
|
||||
def gen_c_headers_and_code(parser, logger, prefix):
|
||||
if prefix == "" or prefix is None:
|
||||
prefix = ""
|
||||
else:
|
||||
prefix = "%s/" % prefix
|
||||
for j in parser.json_files:
|
||||
with open('%s%s' % (prefix, json_to_header_name(j)), "w") as io:
|
||||
gen_json_header(parser, logger, j, io)
|
||||
with open('%s%s' % (prefix, json_to_code_name(j)), "w") as io:
|
||||
gen_json_code(parser, logger, j, io)
|
||||
|
||||
|
||||
def gen_c_unified_headers(parser, logger, prefix):
|
||||
if prefix == "" or prefix is None:
|
||||
prefix = ""
|
||||
else:
|
||||
prefix = "%s/" % prefix
|
||||
for j in parser.json_files:
|
||||
with open('%s%s' % (prefix, json_to_header_name(j)), "w") as io:
|
||||
gen_json_unified_header(parser, logger, j, io)
|
||||
with open('%s%s' % (prefix, json_to_c_header_name(j)), "w") as io:
|
||||
gen_json_unified_header(
|
||||
parser, logger, j, io, json_to_c_header_name(j))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -784,7 +666,7 @@ if __name__ == '__main__':
|
||||
logger = logging.getLogger("VAPI C GEN")
|
||||
logger.setLevel(log_level)
|
||||
|
||||
argparser = argparse.ArgumentParser(description="VPP JSON API parser")
|
||||
argparser = argparse.ArgumentParser(description="VPP C API generator")
|
||||
argparser.add_argument('files', metavar='api-file', action='append',
|
||||
type=str, help='json api file'
|
||||
'(may be specified multiple times)')
|
||||
|
61
src/vpp-api/vapi/vapi_common.h
Normal file
61
src/vpp-api/vapi/vapi_common.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
*------------------------------------------------------------------
|
||||
* 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.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef vapi_common_h_included
|
||||
#define vapi_common_h_included
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAPI_OK = 0, /**< success */
|
||||
VAPI_EINVAL, /**< invalid value encountered */
|
||||
VAPI_EAGAIN, /**< operation would block */
|
||||
VAPI_ENOTSUP, /**< operation not supported */
|
||||
VAPI_ENOMEM, /**< out of memory */
|
||||
VAPI_ENORESP, /**< no response to request */
|
||||
VAPI_EMAP_FAIL, /**< failure while mapping api */
|
||||
VAPI_ECON_FAIL, /**< failure while connecting to vpp */
|
||||
VAPI_EINCOMPATIBLE, /**< fundamental incompatibility while connecting to vpp
|
||||
(control ping/control ping reply mismatch) */
|
||||
VAPI_MUTEX_FAILURE, /**< failure manipulating internal mutex(es) */
|
||||
VAPI_EUSER, /**< user error used for breaking dispatch,
|
||||
never used by VAPI */
|
||||
} vapi_error_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAPI_MODE_BLOCKING = 1, /**< operations block until response received */
|
||||
VAPI_MODE_NONBLOCKING = 2, /**< operations never block */
|
||||
} vapi_mode_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAPI_WAIT_FOR_READ, /**< wait until some message is readable */
|
||||
VAPI_WAIT_FOR_WRITE, /**< wait until a message can be written */
|
||||
VAPI_WAIT_FOR_READ_WRITE, /**< wait until a read or write can be done */
|
||||
} vapi_wait_mode_e;
|
||||
|
||||
typedef int vapi_msg_id_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
262
src/vpp-api/vapi/vapi_cpp_gen.py
Executable file
262
src/vpp-api/vapi/vapi_cpp_gen.py
Executable file
@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from vapi_c_gen import CField, CStruct, CSimpleType, CStructType, CMessage, \
|
||||
json_to_c_header_name
|
||||
from vapi_json_parser import JsonParser
|
||||
|
||||
|
||||
class CppField(CField):
|
||||
def __init__(
|
||||
self,
|
||||
field_name,
|
||||
field_type,
|
||||
array_len=None,
|
||||
nelem_field=None):
|
||||
super().__init__(field_name, field_type, array_len, nelem_field)
|
||||
|
||||
|
||||
class CppStruct(CStruct):
|
||||
def __init__(self, name, fields):
|
||||
super().__init__(name, fields)
|
||||
|
||||
|
||||
class CppSimpleType (CSimpleType):
|
||||
|
||||
def __init__(self, name):
|
||||
super().__init__(name)
|
||||
|
||||
|
||||
class CppStructType (CStructType, CppStruct):
|
||||
def __init__(self, definition, typedict, field_class):
|
||||
super().__init__(definition, typedict, field_class)
|
||||
|
||||
|
||||
class CppMessage (CMessage):
|
||||
def __init__(self, logger, definition, typedict,
|
||||
struct_type_class, simple_type_class, field_class):
|
||||
super().__init__(logger, definition, typedict, struct_type_class,
|
||||
simple_type_class, field_class)
|
||||
|
||||
def get_swap_to_be_template_instantiation(self):
|
||||
return "\n".join([
|
||||
"template <> inline void vapi_swap_to_be<%s>(%s *msg)" %
|
||||
(self.get_c_name(), self.get_c_name()),
|
||||
"{",
|
||||
" %s(msg);" % self.get_swap_to_be_func_name(),
|
||||
"}",
|
||||
])
|
||||
|
||||
def get_swap_to_host_template_instantiation(self):
|
||||
return "\n".join([
|
||||
"template <> inline void vapi_swap_to_host<%s>(%s *msg)" %
|
||||
(self.get_c_name(), self.get_c_name()),
|
||||
"{",
|
||||
" %s(msg);" % self.get_swap_to_host_func_name(),
|
||||
"}",
|
||||
])
|
||||
|
||||
def get_alloc_template_instantiation(self):
|
||||
return "\n".join([
|
||||
"template <> inline %s* vapi_alloc<%s%s>"
|
||||
"(Connection &con%s)" %
|
||||
(self.get_c_name(), self.get_c_name(),
|
||||
", size_t" * len(self.get_alloc_vla_param_names()),
|
||||
"".join([", size_t %s" % n for n in
|
||||
self.get_alloc_vla_param_names()])
|
||||
),
|
||||
"{",
|
||||
" %s* result = %s(con.vapi_ctx%s);" %
|
||||
(self.get_c_name(), self.get_alloc_func_name(),
|
||||
"".join([", %s" % n
|
||||
for n in self.get_alloc_vla_param_names()])),
|
||||
"#if VAPI_CPP_DEBUG_LEAKS",
|
||||
" con.on_shm_data_alloc(result);",
|
||||
"#endif",
|
||||
" return result;",
|
||||
"}",
|
||||
])
|
||||
|
||||
def get_cpp_name(self):
|
||||
return "%s%s" % (self.name[0].upper(), self.name[1:])
|
||||
|
||||
def get_req_template_name(self):
|
||||
if self.is_dump():
|
||||
template = "Dump"
|
||||
else:
|
||||
template = "Request"
|
||||
|
||||
return "%s<%s, %s%s>" % (
|
||||
template,
|
||||
self.get_c_name(),
|
||||
self.reply.get_c_name(),
|
||||
"".join([", size_t"] * len(self.get_alloc_vla_param_names()))
|
||||
)
|
||||
|
||||
def get_req_template_instantiation(self):
|
||||
return "template class %s;" % self.get_req_template_name()
|
||||
|
||||
def get_type_alias(self):
|
||||
return "using %s = %s;" % (
|
||||
self.get_cpp_name(), self.get_req_template_name())
|
||||
|
||||
def get_reply_template_name(self):
|
||||
return "Msg<%s>" % (self.get_c_name())
|
||||
|
||||
def get_reply_type_alias(self):
|
||||
return "using %s = %s;" % (
|
||||
self.get_cpp_name(), self.get_reply_template_name())
|
||||
|
||||
def get_msg_class_instantiation(self):
|
||||
return "template class Msg<%s>;" % self.get_c_name()
|
||||
|
||||
def get_get_msg_id_t_instantiation(self):
|
||||
return "\n".join([
|
||||
("template <> inline vapi_msg_id_t vapi_get_msg_id_t<%s>()"
|
||||
% self.get_c_name()),
|
||||
"{",
|
||||
" return ::%s; " % self.get_msg_id_name(),
|
||||
"}",
|
||||
"",
|
||||
("template <> inline vapi_msg_id_t "
|
||||
"vapi_get_msg_id_t<Msg<%s>>()" % self.get_c_name()),
|
||||
"{",
|
||||
" return ::%s; " % self.get_msg_id_name(),
|
||||
"}",
|
||||
])
|
||||
|
||||
def get_cpp_constructor(self):
|
||||
return '\n'.join([
|
||||
('static void __attribute__((constructor)) '
|
||||
'__vapi_cpp_constructor_%s()'
|
||||
% self.name),
|
||||
'{',
|
||||
(' vapi::vapi_msg_set_msg_id<%s>(%s);' % (
|
||||
self.get_c_name(), self.get_msg_id_name())),
|
||||
'}',
|
||||
])
|
||||
|
||||
|
||||
def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments):
|
||||
logger.info("Generating header `%s'" % io.name)
|
||||
orig_stdout = sys.stdout
|
||||
sys.stdout = io
|
||||
include_guard = "__included_hpp_%s" % (
|
||||
j.replace(".", "_").replace("/", "_").replace("-", "_"))
|
||||
print("#ifndef %s" % include_guard)
|
||||
print("#define %s" % include_guard)
|
||||
print("")
|
||||
print("#include <vapi/vapi.hpp>")
|
||||
print("#include <%s%s>" % (gen_h_prefix, json_to_c_header_name(j)))
|
||||
print("")
|
||||
print("namespace vapi {")
|
||||
print("")
|
||||
for m in parser.messages_by_json[j].values():
|
||||
# utility functions need to go first, otherwise internal instantiation
|
||||
# causes headaches ...
|
||||
if add_debug_comments:
|
||||
print("/* m.get_swap_to_be_template_instantiation() */")
|
||||
print("%s" % m.get_swap_to_be_template_instantiation())
|
||||
print("")
|
||||
if add_debug_comments:
|
||||
print("/* m.get_swap_to_host_template_instantiation() */")
|
||||
print("%s" % m.get_swap_to_host_template_instantiation())
|
||||
print("")
|
||||
if add_debug_comments:
|
||||
print("/* m.get_get_msg_id_t_instantiation() */")
|
||||
print("%s" % m.get_get_msg_id_t_instantiation())
|
||||
print("")
|
||||
if add_debug_comments:
|
||||
print("/* m.get_cpp_constructor() */")
|
||||
print("%s" % m.get_cpp_constructor())
|
||||
print("")
|
||||
if not m.is_reply():
|
||||
if add_debug_comments:
|
||||
print("/* m.get_alloc_template_instantiation() */")
|
||||
print("%s" % m.get_alloc_template_instantiation())
|
||||
print("")
|
||||
if add_debug_comments:
|
||||
print("/* m.get_msg_class_instantiation() */")
|
||||
print("%s" % m.get_msg_class_instantiation())
|
||||
print("")
|
||||
if m.is_reply():
|
||||
if add_debug_comments:
|
||||
print("/* m.get_reply_type_alias() */")
|
||||
print("%s" % m.get_reply_type_alias())
|
||||
continue
|
||||
if add_debug_comments:
|
||||
print("/* m.get_req_template_instantiation() */")
|
||||
print("%s" % m.get_req_template_instantiation())
|
||||
print("")
|
||||
if add_debug_comments:
|
||||
print("/* m.get_type_alias() */")
|
||||
print("%s" % m.get_type_alias())
|
||||
print("")
|
||||
print("}") # namespace vapi
|
||||
|
||||
print("#endif")
|
||||
sys.stdout = orig_stdout
|
||||
|
||||
|
||||
def json_to_cpp_header_name(json_name):
|
||||
if json_name.endswith(".json"):
|
||||
return "%s.vapi.hpp" % os.path.splitext(json_name)[0]
|
||||
raise Exception("Unexpected json name `%s'!" % json_name)
|
||||
|
||||
|
||||
def gen_cpp_headers(parser, logger, prefix, gen_h_prefix,
|
||||
add_debug_comments=False):
|
||||
if prefix == "" or prefix is None:
|
||||
prefix = ""
|
||||
else:
|
||||
prefix = "%s/" % prefix
|
||||
if gen_h_prefix is None:
|
||||
gen_h_prefix = ""
|
||||
else:
|
||||
gen_h_prefix = "%s/" % gen_h_prefix
|
||||
for j in parser.json_files:
|
||||
with open('%s%s' % (prefix, json_to_cpp_header_name(j)), "w") as io:
|
||||
gen_json_header(parser, logger, j, io,
|
||||
gen_h_prefix, add_debug_comments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
verbose = int(os.getenv("V", 0))
|
||||
except:
|
||||
verbose = 0
|
||||
|
||||
if verbose >= 2:
|
||||
log_level = 10
|
||||
elif verbose == 1:
|
||||
log_level = 20
|
||||
else:
|
||||
log_level = 40
|
||||
|
||||
logging.basicConfig(stream=sys.stdout, level=log_level)
|
||||
logger = logging.getLogger("VAPI CPP GEN")
|
||||
logger.setLevel(log_level)
|
||||
|
||||
argparser = argparse.ArgumentParser(description="VPP C++ API generator")
|
||||
argparser.add_argument('files', metavar='api-file', action='append',
|
||||
type=str, help='json api file'
|
||||
'(may be specified multiple times)')
|
||||
argparser.add_argument('--prefix', action='store', default=None,
|
||||
help='path prefix')
|
||||
argparser.add_argument('--gen-h-prefix', action='store', default=None,
|
||||
help='generated C header prefix')
|
||||
args = argparser.parse_args()
|
||||
|
||||
jsonparser = JsonParser(logger, args.files,
|
||||
simple_type_class=CppSimpleType,
|
||||
struct_type_class=CppStructType,
|
||||
field_class=CppField,
|
||||
message_class=CppMessage)
|
||||
|
||||
gen_cpp_headers(jsonparser, logger, args.prefix, args.gen_h_prefix)
|
||||
|
||||
for e in jsonparser.exceptions:
|
||||
logger.error(e)
|
@ -22,6 +22,7 @@
|
||||
#define VAPI_DEBUG (0)
|
||||
#define VAPI_DEBUG_CONNECT (0)
|
||||
#define VAPI_DEBUG_ALLOC (0)
|
||||
#define VAPI_CPP_DEBUG_LEAKS (0)
|
||||
|
||||
#if VAPI_DEBUG
|
||||
#include <stdio.h>
|
||||
|
155
src/vpp-api/vapi/vapi_doc.md
Normal file
155
src/vpp-api/vapi/vapi_doc.md
Normal file
@ -0,0 +1,155 @@
|
||||
# VPP API module {#vapi_doc}
|
||||
|
||||
## Overview
|
||||
|
||||
VPP API module allows communicating with VPP over shared memory interface.
|
||||
The API consists of 3 parts:
|
||||
|
||||
* common code - low-level API
|
||||
* generated code - high-level API
|
||||
* code generator - to generate your own high-level API e.g. for custom plugins
|
||||
|
||||
### Common code
|
||||
|
||||
#### C common code
|
||||
|
||||
C common code represents the basic, low-level API, providing functions to
|
||||
connect/disconnect, perform message discovery and send/receive messages.
|
||||
The C variant is in vapi.h.
|
||||
|
||||
#### C++ common code
|
||||
|
||||
C++ is provided by vapi.hpp and contains high-level API templates,
|
||||
which are specialized by generated code.
|
||||
|
||||
### Generated code
|
||||
|
||||
Each API file present in the source tree is automatically translated to JSON
|
||||
file, which the code generator parses and generates either C (`vapi_c_gen.py`)
|
||||
or C++ (`vapi_cpp_gen.py`) code.
|
||||
|
||||
This can then be included in the client application and provides convenient way
|
||||
to interact with VPP. This includes:
|
||||
|
||||
* automatic byte-swapping
|
||||
* automatic request-response matching based on context
|
||||
* automatic casts to appropriate types (type-safety) when calling callbacks
|
||||
* automatic sending of control-pings for dump messages
|
||||
|
||||
The API supports two modes of operation:
|
||||
|
||||
* blocking
|
||||
* non-blocking
|
||||
|
||||
In blocking mode, whenever an operation is initiated, the code waits until it
|
||||
can finish. This means that when sending a message, the call blocks until
|
||||
the message can be written to shared memory. Similarly, receiving a message
|
||||
blocks until a message becomes available. On higher level, this also means that
|
||||
when doing a request (e.g. `show_version`), the call blocks until a response
|
||||
comes back (e.g. `show_version_reply`).
|
||||
|
||||
In non-blocking mode, these are decoupled, the API returns VAPI_EAGAIN whenever
|
||||
an operation cannot be performed and after sending a request, it's up to
|
||||
the client to wait for and process a response.
|
||||
|
||||
### Code generator
|
||||
|
||||
Python code generator comes in two flavors - C and C++ and generates high-level
|
||||
API headers. All the code is stored in the headers.
|
||||
|
||||
## Usage
|
||||
|
||||
### Low-level API
|
||||
|
||||
Refer to inline API documentation in doxygen format in `vapi.h` header
|
||||
for description of functions. It's recommened to use the safer, high-level
|
||||
API provided by specialized headers (e.g. `vpe.api.vapi.h`
|
||||
or `vpe.api.vapi.hpp`).
|
||||
|
||||
#### C high-level API
|
||||
|
||||
##### Callbacks
|
||||
|
||||
The C high-level API is strictly callback-based for maximum efficiency.
|
||||
Whenever an operation is initiated a callback with a callback context is part
|
||||
of that operation. The callback is then invoked when the response (or multiple
|
||||
responses) arrive which are tied to the request. Also, callbacks are invoked
|
||||
whenever an event arrives, if such callback is registered. All the pointers
|
||||
to responses/events point to shared memory and are immediately freed after
|
||||
callback finishes so the client needs to extract/copy any data in which it
|
||||
is interested in.
|
||||
|
||||
#### Blocking mode
|
||||
|
||||
In simple blocking mode, the whole operation (being a simple request or a dump)
|
||||
is finished and it's callback is called (potentially multiple times for dumps)
|
||||
during function call.
|
||||
|
||||
Example pseudo-code for a simple request in this mode:
|
||||
|
||||
`
|
||||
vapi_show_version(message, callback, callback_context)
|
||||
|
||||
1. generate unique internal context and assign it to message.header.context
|
||||
2. byteswap the message to network byte order
|
||||
3. send message to vpp (message is now consumed and vpp will free it)
|
||||
4. create internal "outstanding request context" which stores the callback,
|
||||
callback context and the internal context value
|
||||
5. call dispatch, which in this mode receives and processes responses until
|
||||
the internal "outstanding requests" queue is empty. In blocking mode, this
|
||||
queue always contains at most one item.
|
||||
`
|
||||
|
||||
**Note**: it's possible for different - unrelated callbacks to be called before
|
||||
the response callbacks is called in cases where e.g. events are stored
|
||||
in shared memory queue.
|
||||
|
||||
#### Non-blocking mode
|
||||
|
||||
In non-blocking mode, all the requests are only byte-swapped and the context
|
||||
information along with callbacks is stored locally (so in the above example,
|
||||
only steps 1-4 are executed and step 5 is skipped). Calling dispatch is up to
|
||||
the client application. This allows to alternate between sending/receiving
|
||||
messages or have a dedicated thread which calls dispatch.
|
||||
|
||||
### C++ high level API
|
||||
|
||||
#### Callbacks
|
||||
|
||||
In C++ API, the response is automatically tied to the corresponding `Request`,
|
||||
`Dump` or `Event_registration` object. Optionally a callback might be specified,
|
||||
which then gets called when the response is received.
|
||||
|
||||
**Note**: responses take up shared memory space and should be freed either
|
||||
manually (in case of result sets) or automatically (by destroying the object
|
||||
owning them) when no longer needed. Once a Request or Dump object was executed,
|
||||
it cannot be re-sent, since the request itself (stores in shared memory)
|
||||
is consumed by vpp and inaccessible (set to nullptr) anymore.
|
||||
|
||||
#### Usage
|
||||
|
||||
#### Requests & dumps
|
||||
|
||||
0. Create on object of `Connection` type and call `connect()` to connect to vpp.
|
||||
1. Create an object of `Request` or `Dump` type using it's typedef (e.g.
|
||||
`Show_version`)
|
||||
2. Use `get_request()` to obtain and manipulate the underlying request if
|
||||
required.
|
||||
3. Issue `execute()` to send the request.
|
||||
4. Use either `wait_for_response()` or `dispatch()` to wait for the response.
|
||||
5. Use `get_response_state()` to get the state and `get_response()` to read
|
||||
the response.
|
||||
|
||||
#### Events
|
||||
|
||||
0. Create a `Connection` and execute the appropriate `Request` to subscribe to
|
||||
events (e.g. `Want_stats`)
|
||||
1. Create an `Event_registration` with a template argument being the type of
|
||||
event you are insterested in.
|
||||
2. Call `dispatch()` or `wait_for_response()` to wait for the event. A callback
|
||||
will be called when an event occurs (if passed to `Event_registration()`
|
||||
constructor). Alternatively, read the result set.
|
||||
|
||||
**Note**: events stored in the result set take up space in shared memory
|
||||
and should be freed regularly (e.g. in the callback, once the event is
|
||||
processed).
|
@ -18,6 +18,7 @@
|
||||
#ifndef VAPI_INTERNAL_H
|
||||
#define VAPI_INTERNAL_H
|
||||
|
||||
#include <endian.h>
|
||||
#include <string.h>
|
||||
#include <vppinfra/types.h>
|
||||
|
||||
@ -31,6 +32,10 @@
|
||||
* time..
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vapi_ctx_s;
|
||||
|
||||
typedef struct __attribute__ ((__packed__))
|
||||
@ -71,7 +76,7 @@ vapi_type_msg_header2_t_ntoh (vapi_type_msg_header2_t * h)
|
||||
}
|
||||
|
||||
|
||||
#include <vapi.h>
|
||||
#include <vapi/vapi.h>
|
||||
|
||||
typedef vapi_error_e (*vapi_cb_t) (struct vapi_ctx_s *, void *, vapi_error_e,
|
||||
bool, void *);
|
||||
@ -85,8 +90,8 @@ typedef struct
|
||||
const char *name_with_crc;
|
||||
size_t name_with_crc_len;
|
||||
bool has_context;
|
||||
size_t context_offset;
|
||||
size_t payload_offset;
|
||||
int context_offset;
|
||||
int payload_offset;
|
||||
size_t size;
|
||||
generic_swap_fn_t swap_to_be;
|
||||
generic_swap_fn_t swap_to_host;
|
||||
@ -102,12 +107,12 @@ typedef struct
|
||||
void (*swap_to_host) (void *payload);
|
||||
} vapi_event_desc_t;
|
||||
|
||||
extern bool *__vapi_msg_is_with_context;
|
||||
|
||||
vapi_msg_id_t vapi_register_msg (vapi_message_desc_t * msg);
|
||||
u16 vapi_lookup_vl_msg_id (vapi_ctx_t ctx, vapi_msg_id_t id);
|
||||
vapi_msg_id_t vapi_lookup_vapi_msg_id_t (vapi_ctx_t ctx, u16 vl_msg_id);
|
||||
int vapi_get_client_index (vapi_ctx_t ctx);
|
||||
bool vapi_is_nonblocking (vapi_ctx_t ctx);
|
||||
bool vapi_requests_empty (vapi_ctx_t ctx);
|
||||
bool vapi_requests_full (vapi_ctx_t ctx);
|
||||
size_t vapi_get_request_count (vapi_ctx_t ctx);
|
||||
size_t vapi_get_max_request_count (vapi_ctx_t ctx);
|
||||
@ -119,8 +124,15 @@ void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *payload);
|
||||
void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *payload);
|
||||
size_t vapi_get_message_size (vapi_msg_id_t id);
|
||||
size_t vapi_get_context_offset (vapi_msg_id_t id);
|
||||
bool vapi_msg_is_with_context (vapi_msg_id_t id);
|
||||
size_t vapi_get_message_count();
|
||||
const char *vapi_get_msg_name(vapi_msg_id_t id);
|
||||
|
||||
vapi_error_e vapi_producer_lock (vapi_ctx_t ctx);
|
||||
vapi_error_e vapi_producer_unlock (vapi_ctx_t ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -90,6 +90,7 @@ class Message:
|
||||
|
||||
def __init__(self, logger, definition, typedict,
|
||||
struct_type_class, simple_type_class, field_class):
|
||||
self.request = None
|
||||
self.logger = logger
|
||||
m = definition
|
||||
logger.debug("Parsing message definition `%s'" % m)
|
||||
@ -292,6 +293,7 @@ class JsonParser:
|
||||
if not m.is_reply():
|
||||
try:
|
||||
m.reply = self.get_reply(n)
|
||||
m.reply.request = m
|
||||
except:
|
||||
raise ParseError(
|
||||
"Cannot find reply to message `%s'" % n)
|
||||
|
@ -1,17 +1,31 @@
|
||||
BINDIR = $(BR)/vapi_test/
|
||||
BIN = $(addprefix $(BINDIR), vapi_test)
|
||||
LIBS = -L$(VPP_TEST_BUILD_DIR)/vpp/.libs/ -L$(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/ -lvppinfra -lvlibmemoryclient -lsvm -lpthread -lcheck -lsubunit -lrt -lm -lvapiclient
|
||||
CFLAGS = -ggdb -O0 -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi -I$(WS_ROOT)/src/vpp-api/vapi/
|
||||
CBIN = $(addprefix $(BINDIR), vapi_c_test)
|
||||
CPPBIN = $(addprefix $(BINDIR), vapi_cpp_test)
|
||||
|
||||
all: $(BIN)
|
||||
LIBS = -L$(VPP_TEST_BUILD_DIR)/vpp/.libs/ -L$(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/ -lvppinfra -lvlibmemoryclient -lsvm -lpthread -lcheck -lsubunit -lrt -lm -lvapiclient
|
||||
CFLAGS = -std=gnu99 -g -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_INSTALL_PATH)/vpp/include -I$(BINDIR)
|
||||
CPPFLAGS = -std=c++11 -g -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_INSTALL_PATH)/vpp/include -I$(BINDIR)
|
||||
|
||||
all: $(CBIN) $(CPPBIN)
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
|
||||
SRC = vapi_test.c
|
||||
CSRC = vapi_c_test.c
|
||||
|
||||
$(BIN): $(SRC) $(BINDIR) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so
|
||||
gcc -ggdb -o $@ $(SRC) $(CFLAGS) $(LIBS)
|
||||
fake.api.vapi.h: fake.api.json $(BINDIR) $(WS_ROOT)/src/vpp-api/vapi/vapi_c_gen.py
|
||||
$(WS_ROOT)/src/vpp-api/vapi/vapi_c_gen.py --prefix $(BINDIR) $<
|
||||
|
||||
fake.api.vapi.hpp: fake.api.json $(BINDIR) $(WS_ROOT)/src/vpp-api/vapi/vapi_cpp_gen.py
|
||||
$(WS_ROOT)/src/vpp-api/vapi/vapi_cpp_gen.py --prefix $(BINDIR) $<
|
||||
|
||||
$(CBIN): $(CSRC) $(BINDIR) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so fake.api.vapi.h
|
||||
$(CC) -o $@ $(CFLAGS) $(CSRC) $(LIBS)
|
||||
|
||||
CPPSRC = vapi_cpp_test.cpp
|
||||
|
||||
$(CPPBIN): $(CPPSRC) $(BINDIR) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so fake.api.vapi.hpp
|
||||
$(CXX) -o $@ $(CPPFLAGS) $(CPPSRC) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -rf $(BINDIR)
|
||||
|
35
test/ext/fake.api.json
Normal file
35
test/ext/fake.api.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"types" : [
|
||||
|
||||
],
|
||||
"messages" : [
|
||||
["test_fake_msg",
|
||||
["u16", "_vl_msg_id"],
|
||||
["u32", "client_index"],
|
||||
["u32", "context"],
|
||||
["u8", "dummy", 256],
|
||||
{"crc" : "0xcafebafe"}
|
||||
],
|
||||
["test_fake_msg_reply",
|
||||
["u16", "_vl_msg_id"],
|
||||
["u32", "context"],
|
||||
["i32", "retval"],
|
||||
{"crc" : "0xcafebafe"}
|
||||
],
|
||||
["test_fake_dump",
|
||||
["u16", "_vl_msg_id"],
|
||||
["u32", "client_index"],
|
||||
["u32", "context"],
|
||||
["u32", "dummy"],
|
||||
{"crc" : "0xcafebafe"}
|
||||
],
|
||||
["test_fake_details",
|
||||
["u16", "_vl_msg_id"],
|
||||
["u32", "client_index"],
|
||||
["u32", "context"],
|
||||
["u32", "dummy"],
|
||||
{"crc" : "0xcafebafe"}
|
||||
]
|
||||
],
|
||||
"vl_api_version" :"0x224c7aad"
|
||||
}
|
@ -22,16 +22,18 @@
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <check.h>
|
||||
#include <vpp-api/vapi/vapi.h>
|
||||
#include <vpe.api.vapi.h>
|
||||
#include <interface.api.vapi.h>
|
||||
#include <l2.api.vapi.h>
|
||||
#include <stats.api.vapi.h>
|
||||
#include <vapi/vapi.h>
|
||||
#include <vapi/vpe.api.vapi.h>
|
||||
#include <vapi/interface.api.vapi.h>
|
||||
#include <vapi/l2.api.vapi.h>
|
||||
#include <vapi/stats.api.vapi.h>
|
||||
#include <fake.api.vapi.h>
|
||||
|
||||
DEFINE_VAPI_MSG_IDS_VPE_API_JSON;
|
||||
DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON;
|
||||
DEFINE_VAPI_MSG_IDS_L2_API_JSON;
|
||||
DEFINE_VAPI_MSG_IDS_STATS_API_JSON;
|
||||
DEFINE_VAPI_MSG_IDS_FAKE_API_JSON;
|
||||
|
||||
static char *app_name = NULL;
|
||||
static char *api_prefix = NULL;
|
||||
@ -521,9 +523,8 @@ START_TEST (test_show_version_1)
|
||||
size_t size;
|
||||
rv = vapi_recv (ctx, (void *) &resp, &size);
|
||||
ck_assert_int_eq (VAPI_OK, rv);
|
||||
vapi_payload_show_version_reply *payload = &resp->payload;
|
||||
int dummy;
|
||||
show_version_cb (NULL, &dummy, VAPI_OK, true, payload);
|
||||
show_version_cb (NULL, &dummy, VAPI_OK, true, &resp->payload);
|
||||
vapi_msg_free (ctx, resp);
|
||||
}
|
||||
|
||||
@ -1069,6 +1070,16 @@ START_TEST (test_no_response_2)
|
||||
}
|
||||
|
||||
END_TEST;
|
||||
|
||||
START_TEST (test_unsupported)
|
||||
{
|
||||
printf ("--- Unsupported messages ---\n");
|
||||
bool available = vapi_is_msg_available (ctx, vapi_msg_id_test_fake_msg);
|
||||
ck_assert_int_eq (false, available);
|
||||
}
|
||||
|
||||
END_TEST;
|
||||
|
||||
Suite *
|
||||
test_suite (void)
|
||||
{
|
||||
@ -1115,6 +1126,11 @@ test_suite (void)
|
||||
tcase_add_test (tc_nonblock, test_no_response_2);
|
||||
suite_add_tcase (s, tc_nonblock);
|
||||
|
||||
TCase *tc_unsupported = tcase_create ("Unsupported message");
|
||||
tcase_add_checked_fixture (tc_unsupported, setup_blocking, teardown);
|
||||
tcase_add_test (tc_unsupported, test_unsupported);
|
||||
suite_add_tcase (s, tc_unsupported);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
591
test/ext/vapi_cpp_test.cpp
Normal file
591
test/ext/vapi_cpp_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -45,17 +45,45 @@ class Worker(Thread):
|
||||
class VAPITestCase(VppTestCase):
|
||||
""" VAPI test """
|
||||
|
||||
def test_vapi(self):
|
||||
""" run VAPI tests """
|
||||
def test_vapi_c(self):
|
||||
""" run C VAPI tests """
|
||||
var = "BR"
|
||||
built_root = os.getenv(var, None)
|
||||
self.assertIsNotNone(built_root,
|
||||
"Environment variable `%s' not set" % var)
|
||||
executable = "%s/vapi_test/vapi_test" % built_root
|
||||
executable = "%s/vapi_test/vapi_c_test" % built_root
|
||||
worker = Worker(
|
||||
[executable, "vapi client", self.shm_prefix], self.logger)
|
||||
worker.start()
|
||||
timeout = 45
|
||||
timeout = 60
|
||||
worker.join(timeout)
|
||||
self.logger.info("Worker result is `%s'" % worker.result)
|
||||
error = False
|
||||
if worker.result is None:
|
||||
try:
|
||||
error = True
|
||||
self.logger.error(
|
||||
"Timeout! Worker did not finish in %ss" % timeout)
|
||||
os.killpg(os.getpgid(worker.process.pid), signal.SIGTERM)
|
||||
worker.join()
|
||||
except:
|
||||
raise Exception("Couldn't kill worker-spawned process")
|
||||
if error:
|
||||
raise Exception(
|
||||
"Timeout! Worker did not finish in %ss" % timeout)
|
||||
self.assert_equal(worker.result, 0, "Binary test return code")
|
||||
|
||||
def test_vapi_cpp(self):
|
||||
""" run C++ VAPI tests """
|
||||
var = "BR"
|
||||
built_root = os.getenv(var, None)
|
||||
self.assertIsNotNone(built_root,
|
||||
"Environment variable `%s' not set" % var)
|
||||
executable = "%s/vapi_test/vapi_cpp_test" % built_root
|
||||
worker = Worker(
|
||||
[executable, "vapi client", self.shm_prefix], self.logger)
|
||||
worker.start()
|
||||
timeout = 120
|
||||
worker.join(timeout)
|
||||
self.logger.info("Worker result is `%s'" % worker.result)
|
||||
error = False
|
||||
|
Reference in New Issue
Block a user