Add C++ API

Change-Id: Iff634f22d43470e2dc028387b3816257fd7b4156
Signed-off-by: Klement Sekera <ksekera@cisco.com>
This commit is contained in:
Klement Sekera
2017-06-12 06:49:33 +02:00
committed by Neale Ranns
parent 9d063047eb
commit dc15be2ca7
20 changed files with 2341 additions and 271 deletions

38
.clang-format Normal file
View 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
...

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: *;
};

View File

@ -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
*

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)')

View 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
View 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)

View File

@ -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>

View 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).

View File

@ -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

View File

@ -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)

View File

@ -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
View 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"
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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