From: Dave Barach Date: Thu, 10 Nov 2016 19:22:49 +0000 (-0500) Subject: Add client-side msg_name_and_crc -> msg_index table X-Git-Tag: v17.01-rc1~201 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=557d128b68a1213e056f5eed9fe6f230ca3f3144;p=vpp.git Add client-side msg_name_and_crc -> msg_index table vppapigen now generates per-message crcs. Verified that whitespace and real changes in message A don't change the crc for message B, etc. Fixed the sample and flowperpkt plugins to participate. Others need the same treatment. They don't build due to python/java language binding build issues. To use the scheme: Client connects as usual. Then call: u32 vl_api_get_msg_index(char * name_and_crc) name_and_crc is a string like: "flowperpkt_tx_interface_add_del_753301f3", aka the message name with _%08x appended. Try these vpp-api-test commands to play with it: vat# dump_msg_api_table [366]: punt_reply_cca27fbe [367]: ipsec_spd_dump_5e9ae88e [368]: ipsec_spd_details_6f7821b0 [369]: sample_macswap_enable_disable_0f2813e2 [370]: sample_macswap_enable_disable_reply_476738e5 [371]: flowperpkt_tx_interface_add_del_753301f3 [372]: flowperpkt_tx_interface_add_del_reply_d47e6e0b vat# get_msg_id sample_macswap_enable_disable_reply_476738e5 'sample_macswap_enable_disable_reply_476738e5' has message index 370 vat# get_msg_id sample_macswap_enable_disable_reply_476738e3 'sample_macswap_enable_disable_reply_476738e3' not found CRCs may vary, etc. vppapigen is used to build a set of JSON representations of each API file from vpp-api/Makefile.am and that is in turn used by each language binding (Java, Python, Lua). Change-Id: I3d64582e779dac5f20cddec79c562c288d8fd9c6 Signed-off-by: Dave Barach Signed-off-by: Ole Troan --- diff --git a/build-root/emacs-lisp/plugin-main-skel.el b/build-root/emacs-lisp/plugin-main-skel.el index 196f8653817..47240695d4f 100644 --- a/build-root/emacs-lisp/plugin-main-skel.el +++ b/build-root/emacs-lisp/plugin-main-skel.el @@ -23,7 +23,7 @@ nil '(setq PLUGIN-NAME (upcase plugin-name)) " /* - * " plugin-name ".c - skeleton vpp engine plug-in + * " plugin-name ".c - skeleton vpp engine plug-in * * Copyright (c) * Licensed under the Apache License, Version 2.0 (the \"License\"); @@ -52,18 +52,18 @@ nil /* define message structures */ #define vl_typedefs -#include <" plugin-name "/" plugin-name "_all_api_h.h> +#include <" plugin-name "/" plugin-name "_all_api_h.h> #undef vl_typedefs /* define generated endian-swappers */ #define vl_endianfun -#include <" plugin-name "/" plugin-name "_all_api_h.h> +#include <" plugin-name "/" plugin-name "_all_api_h.h> #undef vl_endianfun /* instantiate all the print functions we know about */ #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) #define vl_printfun -#include <" plugin-name "/" plugin-name "_all_api_h.h> +#include <" plugin-name "/" plugin-name "_all_api_h.h> #undef vl_printfun /* Get the API version number */ @@ -71,7 +71,7 @@ nil #include <" plugin-name "/" plugin-name "_all_api_h.h> #undef vl_api_version -/* +/* * A handy macro to set up a message reply. * Assumes that the following variables are available: * mp - pointer to request message @@ -100,14 +100,14 @@ do { \\ #define foreach_" plugin-name "_plugin_api_msg \\ _(" PLUGIN-NAME "_ENABLE_DISABLE, " plugin-name "_enable_disable) -/* +/* * This routine exists to convince the vlib plugin framework that * we haven't accidentally copied a random .dll into the plugin directory. * * Also collects global variable pointers passed from the vpp engine */ -clib_error_t * +clib_error_t * vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h, int from_early_init) { @@ -130,7 +130,7 @@ int " plugin-name "_enable_disable (" plugin-name "_main_t * sm, u32 sw_if_index int rv = 0; /* Utterly wrong? */ - if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces, + if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces, sw_if_index)) return VNET_API_ERROR_INVALID_SW_IF_INDEX; @@ -138,7 +138,7 @@ int " plugin-name "_enable_disable (" plugin-name "_main_t * sm, u32 sw_if_index sw = vnet_get_sw_interface (sm->vnet_main, sw_if_index); if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE) return VNET_API_ERROR_INVALID_SW_IF_INDEX; - + vnet_feature_enable_disable (\"device-input\", \"" plugin-name "\", sw_if_index, enable_disable, 0, 0); @@ -153,7 +153,7 @@ static clib_error_t * " plugin-name "_main_t * sm = &" plugin-name "_main; u32 sw_if_index = ~0; int enable_disable = 1; - + int rv; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -168,7 +168,7 @@ static clib_error_t * if (sw_if_index == ~0) return clib_error_return (0, \"Please specify an interface...\"); - + rv = " plugin-name "_enable_disable (sm, sw_if_index, enable_disable); switch(rv) { @@ -176,7 +176,7 @@ static clib_error_t * break; case VNET_API_ERROR_INVALID_SW_IF_INDEX: - return clib_error_return + return clib_error_return (0, \"Invalid interface, only works on physical ports\"); break; @@ -193,7 +193,7 @@ static clib_error_t * VLIB_CLI_COMMAND (" plugin-name "_enable_disable_command, static) = { .path = \"" plugin-name " enable-disable\", - .short_help = + .short_help = \"" plugin-name " enable-disable [disable]\", .function = " plugin-name "_enable_disable_command_fn, }; @@ -206,9 +206,9 @@ static void vl_api_" plugin-name "_enable_disable_t_handler " plugin-name "_main_t * sm = &" plugin-name "_main; int rv; - rv = " plugin-name "_enable_disable (sm, ntohl(mp->sw_if_index), + rv = " plugin-name "_enable_disable (sm, ntohl(mp->sw_if_index), (int) (mp->enable_disable)); - + REPLY_MACRO(VL_API_" PLUGIN-NAME "_ENABLE_DISABLE_REPLY); } @@ -224,13 +224,26 @@ static clib_error_t * vl_noop_handler, \\ vl_api_##n##_t_endian, \\ vl_api_##n##_t_print, \\ - sizeof(vl_api_##n##_t), 1); + sizeof(vl_api_##n##_t), 1); foreach_" plugin-name "_plugin_api_msg; #undef _ return 0; } +#define vl_msg_name_crc_list +#include <" plugin-name "/" plugin-name "_all_api_h.h> +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (" plugin-name "_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_" plugin-name" ; +#undef _ +} + static clib_error_t * " plugin-name "_init (vlib_main_t * vm) { " plugin-name "_main_t * sm = &" plugin-name "_main; @@ -240,7 +253,7 @@ static clib_error_t * " plugin-name "_init (vlib_main_t * vm) name = format (0, \"" plugin-name "_%08x%c\", api_version, 0); /* Ask for a correctly-sized block of API message decode slots */ - sm->msg_id_base = vl_msg_api_get_msg_ids + sm->msg_id_base = vl_msg_api_get_msg_ids ((char *) name, VL_MSG_FIRST_AVAILABLE); error = " plugin-name "_plugin_api_hookup (vm); @@ -252,7 +265,7 @@ static clib_error_t * " plugin-name "_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (" plugin-name "_init); -VNET_FEATURE_INIT (" plugin-name ", static) = +VNET_FEATURE_INIT (" plugin-name ", static) = { .arc_name = \"device-input\", .node_name = \"" plugin-name "\", diff --git a/plugins/flowperpkt-plugin/flowperpkt/flowperpkt.c b/plugins/flowperpkt-plugin/flowperpkt/flowperpkt.c index dbdb189865d..529b9195b46 100644 --- a/plugins/flowperpkt-plugin/flowperpkt/flowperpkt.c +++ b/plugins/flowperpkt-plugin/flowperpkt/flowperpkt.c @@ -438,6 +438,19 @@ flowperpkt_plugin_api_hookup (vlib_main_t * vm) return 0; } +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (flowperpkt_main_t * fm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base); + foreach_vl_msg_name_crc_flowperpkt; +#undef _ +} + /** * @brief Set up the API message handling tables * @param vm vlib_main_t * vlib main data structure pointer @@ -462,6 +475,9 @@ flowperpkt_init (vlib_main_t * vm) /* Hook up message handlers */ error = flowperpkt_plugin_api_hookup (vm); + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (fm, &api_main); + vec_free (name); /* Decide how many worker threads we have */ diff --git a/plugins/ioam-plugin/ioam/export/ioam_export.c b/plugins/ioam-plugin/ioam/export/ioam_export.c index 9efcab05487..e51c43eb4c1 100644 --- a/plugins/ioam-plugin/ioam/export/ioam_export.c +++ b/plugins/ioam-plugin/ioam/export/ioam_export.c @@ -176,6 +176,19 @@ ioam_export_plugin_api_hookup (vlib_main_t * vm) return 0; } +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (ioam_export_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_ioam_export; +#undef _ +} + static clib_error_t * set_ioam_export_ipfix_command_fn (vlib_main_t * vm, unformat_input_t * input, @@ -247,6 +260,9 @@ ioam_export_init (vlib_main_t * vm) error = ioam_export_plugin_api_hookup (vm); + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (em, &api_main); + /* Hook this export node to ip6-hop-by-hop */ ip6_hbyh_node = vlib_get_node_by_name (vm, (u8 *) "ip6-hop-by-hop"); em->my_hbh_slot = vlib_node_add_next (vm, ip6_hbyh_node->index, node_index); diff --git a/plugins/ioam-plugin/ioam/lib-pot/pot_api.c b/plugins/ioam-plugin/ioam/lib-pot/pot_api.c index 95be9e02adf..484f223603e 100644 --- a/plugins/ioam-plugin/ioam/lib-pot/pot_api.c +++ b/plugins/ioam-plugin/ioam/lib-pot/pot_api.c @@ -206,6 +206,19 @@ pot_plugin_api_hookup (vlib_main_t *vm) return 0; } +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (pot_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_pot; +#undef _ +} + static clib_error_t * pot_init (vlib_main_t * vm) { pot_main_t * sm = &pot_main; @@ -222,6 +235,9 @@ static clib_error_t * pot_init (vlib_main_t * vm) error = pot_plugin_api_hookup (vm); + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (sm, &api_main); + vec_free(name); return error; diff --git a/plugins/ioam-plugin/ioam/lib-trace/trace_api.c b/plugins/ioam-plugin/ioam/lib-trace/trace_api.c index b98e65fb320..32c8e23fae3 100644 --- a/plugins/ioam-plugin/ioam/lib-trace/trace_api.c +++ b/plugins/ioam-plugin/ioam/lib-trace/trace_api.c @@ -178,6 +178,19 @@ trace_plugin_api_hookup (vlib_main_t * vm) return 0; } +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (trace_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_trace; +#undef _ +} + static clib_error_t * trace_init (vlib_main_t * vm) { @@ -195,6 +208,9 @@ trace_init (vlib_main_t * vm) error = trace_plugin_api_hookup (vm); + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (sm, &api_main); + vec_free (name); return error; diff --git a/plugins/lb-plugin/lb/api.c b/plugins/lb-plugin/lb/api.c index 56806d25d0c..06c53fa1005 100644 --- a/plugins/lb-plugin/lb/api.c +++ b/plugins/lb-plugin/lb/api.c @@ -47,6 +47,19 @@ typedef enum { #include #undef vl_api_version +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (lb_main_t * lbm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + lbm->msg_id_base); + foreach_vl_msg_name_crc_lb; +#undef _ +} + /* Macro to finish up custom dump fns */ #define FINISH \ vec_add1 (s, 0); \ @@ -206,6 +219,9 @@ static clib_error_t * lb_api_init (vlib_main_t * vm) foreach_lb_plugin_api_msg; #undef _ + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (lbm, &api_main); + return 0; } diff --git a/plugins/sample-plugin/sample/sample.c b/plugins/sample-plugin/sample/sample.c index 321b66c0fb1..603cb2d0ee4 100644 --- a/plugins/sample-plugin/sample/sample.c +++ b/plugins/sample-plugin/sample/sample.c @@ -210,6 +210,19 @@ sample_plugin_api_hookup (vlib_main_t *vm) return 0; } +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (sample_main_t * sm, api_main_t *am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_sample; +#undef _ +} + static clib_error_t * sample_init (vlib_main_t * vm) { sample_main_t * sm = &sample_main; @@ -224,6 +237,9 @@ static clib_error_t * sample_init (vlib_main_t * vm) error = sample_plugin_api_hookup (vm); + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (sm, &api_main); + vec_free(name); return error; diff --git a/plugins/snat-plugin/Makefile.am b/plugins/snat-plugin/Makefile.am index 9f41193a708..0a0fff86486 100644 --- a/plugins/snat-plugin/Makefile.am +++ b/plugins/snat-plugin/Makefile.am @@ -39,8 +39,8 @@ SUFFIXES = .api.h .api %.py: %.api $(info Creating Python binding for $@) - $(CC) $(CPPFLAGS) -E -P -C -x c $< \ - | vppapigen --input - --python - \ + $(CC) $(CPPFLAGS) -E -P -C -x c $< \ + | vppapigen --input - --python - \ | pyvppapigen.py --input - > $@ pyapidir = ${prefix}/vpp_papi_plugins @@ -59,7 +59,8 @@ install-data-hook: @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES)) @(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES)) - +apidir = $(prefix)/snat +api_DATA = snat/snat.api # # Java code generation diff --git a/plugins/snat-plugin/snat/snat.c b/plugins/snat-plugin/snat/snat.c index 66c03817ca0..2956e24f054 100644 --- a/plugins/snat-plugin/snat/snat.c +++ b/plugins/snat-plugin/snat/snat.c @@ -955,6 +955,19 @@ snat_plugin_api_hookup (vlib_main_t *vm) return 0; } +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (snat_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_snat; +#undef _ +} + static void plugin_custom_dump_configure (snat_main_t * sm) { #define _(n,f) sm->api_main->msg_print_handlers \ @@ -985,6 +998,10 @@ static clib_error_t * snat_init (vlib_main_t * vm) sm->api_main = &api_main; error = snat_plugin_api_hookup (vm); + + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (sm, &api_main); + plugin_custom_dump_configure (sm); vec_free(name); diff --git a/vlib-api/vlibapi/api.h b/vlib-api/vlibapi/api.h index a932bf42177..10ca443b434 100644 --- a/vlib-api/vlibapi/api.h +++ b/vlib-api/vlibapi/api.h @@ -87,11 +87,14 @@ typedef struct u8 **traces; } vl_api_trace_t; -typedef CLIB_PACKED (struct - { - u8 endian; u8 wrapped; - u32 nitems; - }) vl_api_trace_file_header_t; +/* *INDENT-OFF* */ +typedef CLIB_PACKED +(struct + { + u8 endian; u8 wrapped; + u32 nitems; +}) vl_api_trace_file_header_t; +/* *INDENT-ON* */ typedef enum { @@ -132,6 +135,8 @@ typedef struct struct vl_shmem_hdr_ *shmem_hdr; vl_api_registration_t **vl_clients; + u8 *serialized_message_table_in_shmem; + /* For plugin msg allocator */ u16 first_available_msg_id; @@ -178,6 +183,9 @@ typedef struct i32 vlib_signal; + /* client side message index hash table */ + uword *msg_index_by_name_and_crc; + char *region_name; char *root_path; } api_main_t; @@ -188,6 +196,7 @@ typedef struct { int id; char *name; + u32 crc; void *handler; void *cleanup; void *endian; @@ -242,6 +251,8 @@ int vl_msg_api_pd_handler (void *mp, int rv); void vl_msg_api_set_first_available_msg_id (u16 first_avail); u16 vl_msg_api_get_msg_ids (char *name, int n); +void vl_msg_api_add_msg_name_crc (api_main_t * am, char *string, u32 id); +u32 vl_api_get_msg_index (u8 * name_and_crc); /* node_serialize.c prototypes */ u8 *vlib_node_serialize (vlib_node_main_t * nm, u8 * vector, diff --git a/vlib-api/vlibapi/api_shared.c b/vlib-api/vlibapi/api_shared.c index 511f693849d..2b2d81c2735 100644 --- a/vlib-api/vlibapi/api_shared.c +++ b/vlib-api/vlibapi/api_shared.c @@ -1312,6 +1312,25 @@ vl_msg_api_get_msg_ids (char *name, int n) return rv; } +void +vl_msg_api_add_msg_name_crc (api_main_t * am, char *string, u32 id) +{ + uword *p; + + if (am->msg_index_by_name_and_crc == 0) + am->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword)); + + p = hash_get_mem (am->msg_index_by_name_and_crc, string); + if (p) + { + clib_warning ("attempt to redefine '%s' ignored...", string); + return; + } + + hash_set_mem (am->msg_index_by_name_and_crc, string, id); +} + + /* * fd.io coding-style-patch-verification: ON * diff --git a/vlib-api/vlibmemory/memclnt.api b/vlib-api/vlibmemory/memclnt.api index c2758238a96..2f654caf2c1 100644 --- a/vlib-api/vlibmemory/memclnt.api +++ b/vlib-api/vlibmemory/memclnt.api @@ -28,9 +28,10 @@ define memclnt_create { define memclnt_create_reply { i32 response; /* Non-negative = success */ - u64 handle; /* handle by which vlib knows this client */ + u64 handle; /* handle by which vlib knows this client */ u32 index; /* index, used e.g. by API trace replay */ u32 context; /* opaque value from the create request */ + u64 message_table; /* serialized message table in shmem */ }; /* diff --git a/vlib-api/vlibmemory/memory_shared.c b/vlib-api/vlibmemory/memory_shared.c index 134fcd52c84..6adc69fc05f 100644 --- a/vlib-api/vlibmemory/memory_shared.c +++ b/vlib-api/vlibmemory/memory_shared.c @@ -541,28 +541,62 @@ vl_msg_api_send_shmem_nolock (unix_shared_memory_queue_t * q, u8 * elem) static void vl_api_memclnt_create_reply_t_handler (vl_api_memclnt_create_reply_t * mp) { + serialize_main_t _sm, *sm = &_sm; api_main_t *am = &api_main; - int rv; + u8 *tblv; + u32 nmsgs; + int i; + u8 *name_and_crc; + u32 msg_index; am->my_client_index = mp->index; am->my_registration = (vl_api_registration_t *) (uword) mp->handle; - rv = ntohl (mp->response); + /* Clean out any previous hash table (unlikely) */ + if (am->msg_index_by_name_and_crc) + { + int i; + u8 **keys = 0; + hash_pair_t *hp; + /* *INDENT-OFF* */ + hash_foreach_pair (hp, am->msg_index_by_name_and_crc, + ({ + vec_add1 (keys, (u8 *) hp->key); + })); + /* *INDENT-ON* */ + for (i = 0; i < vec_len (keys); i++) + vec_free (keys[i]); + vec_free (keys); + } - if (rv < 0) - clib_warning ("WARNING: API mismatch detected"); -} + am->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword)); -void vl_client_add_api_signatures (vl_api_memclnt_create_t * mp) - __attribute__ ((weak)); + /* Recreate the vnet-side API message handler table */ + tblv = (u8 *) mp->message_table; + serialize_open_vector (sm, tblv); + unserialize_integer (sm, &nmsgs, sizeof (u32)); -void -vl_client_add_api_signatures (vl_api_memclnt_create_t * mp) + for (i = 0; i < nmsgs; i++) + { + msg_index = unserialize_likely_small_unsigned_integer (sm); + unserialize_cstring (sm, (char **) &name_and_crc); + hash_set_mem (am->msg_index_by_name_and_crc, name_and_crc, msg_index); + } +} + +u32 +vl_api_get_msg_index (u8 * name_and_crc) { - int i; + api_main_t *am = &api_main; + uword *p; - for (i = 0; i < ARRAY_LEN (mp->api_versions); i++) - mp->api_versions[i] = 0; + if (am->msg_index_by_name_and_crc) + { + p = hash_get_mem (am->msg_index_by_name_and_crc, name_and_crc); + if (p) + return p[0]; + } + return ~0; } int @@ -617,8 +651,6 @@ vl_client_connect (char *name, int ctx_quota, int input_queue_size) mp->input_queue = (uword) vl_input_queue; strncpy ((char *) mp->name, name, sizeof (mp->name) - 1); - vl_client_add_api_signatures (mp); - vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & mp); while (1) diff --git a/vlib-api/vlibmemory/memory_vlib.c b/vlib-api/vlibmemory/memory_vlib.c index ca3eb141f7e..5f97f1611d1 100644 --- a/vlib-api/vlibmemory/memory_vlib.c +++ b/vlib-api/vlibmemory/memory_vlib.c @@ -100,13 +100,28 @@ vl_msg_api_send (vl_api_registration_t * rp, u8 * elem) } } -int vl_msg_api_version_check (vl_api_memclnt_create_t * mp) - __attribute__ ((weak)); - -int -vl_msg_api_version_check (vl_api_memclnt_create_t * mp) +u8 * +vl_api_serialize_message_table (api_main_t * am, u8 * vector) { - return 0; + serialize_main_t _sm, *sm = &_sm; + hash_pair_t *hp; + u32 nmsg = hash_elts (am->msg_index_by_name_and_crc); + + serialize_open_vector (sm, vector); + + /* serialize the count */ + serialize_integer (sm, nmsg, sizeof (u32)); + + hash_foreach_pair (hp, am->msg_index_by_name_and_crc, ( + { + serialize_likely_small_unsigned_integer + (sm, hp->value[0]); + serialize_cstring + (sm, + (char *) hp->key); + })); + + return serialize_close_vector (sm); } /* @@ -120,12 +135,10 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp) vl_api_memclnt_create_reply_t *rp; svm_region_t *svm; unix_shared_memory_queue_t *q; - int rv; + int rv = 0; void *oldheap; api_main_t *am = &api_main; - - /* Indicate API version mismatch if appropriate */ - rv = vl_msg_api_version_check (mp); + u8 *serialized_message_table = 0; /* * This is tortured. Maintain a vlib-address-space private @@ -157,6 +170,9 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp) svm = am->vlib_rp; + if (am->serialized_message_table_in_shmem == 0) + serialized_message_table = vl_api_serialize_message_table (am, 0); + pthread_mutex_lock (&svm->mutex); oldheap = svm_push_data_heap (svm); *regpp = clib_mem_alloc (sizeof (vl_api_registration_t)); @@ -171,10 +187,15 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp) regp->name = format (0, "%s", mp->name); vec_add1 (regp->name, 0); + if (serialized_message_table) + am->serialized_message_table_in_shmem = + vec_dup (serialized_message_table); pthread_mutex_unlock (&svm->mutex); svm_pop_heap (oldheap); + vec_free (serialized_message_table); + rp = vl_msg_api_alloc (sizeof (*rp)); rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY); rp->handle = (uword) regp; @@ -183,6 +204,7 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp) am->shmem_hdr->application_restarts); rp->context = mp->context; rp->response = ntohl (rv); + rp->message_table = (u64) am->serialized_message_table_in_shmem; vl_msg_api_send_shmem (q, (u8 *) & rp); } diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c index 3d7ce4686c8..00bb976b2bf 100644 --- a/vpp-api-test/vat/api_format.c +++ b/vpp-api-test/vat/api_format.c @@ -16534,6 +16534,67 @@ dump_node_table (vat_main_t * vam) return 0; } +static int +value_sort_cmp (void *a1, void *a2) +{ + name_sort_t *n1 = a1; + name_sort_t *n2 = a2; + + if (n1->value < n2->value) + return -1; + if (n1->value > n2->value) + return 1; + return 0; +} + + +static int +dump_msg_api_table (vat_main_t * vam) +{ + api_main_t *am = &api_main; + name_sort_t *nses = 0, *ns; + hash_pair_t *hp; + int i; + + /* *INDENT-OFF* */ + hash_foreach_pair (hp, am->msg_index_by_name_and_crc, + ({ + vec_add2 (nses, ns, 1); + ns->name = (u8 *)(hp->key); + ns->value = (u32) hp->value[0]; + })); + /* *INDENT-ON* */ + + vec_sort_with_function (nses, value_sort_cmp); + + for (i = 0; i < vec_len (nses); i++) + fformat (vam->ofp, " [%d]: %s\n", nses[i].value, nses[i].name); + vec_free (nses); + return 0; +} + +static int +get_msg_id (vat_main_t * vam) +{ + u8 *name_and_crc; + u32 message_index; + + if (unformat (vam->input, "%s", &name_and_crc)) + { + message_index = vl_api_get_msg_index (name_and_crc); + if (message_index == ~0) + { + fformat (vam->ofp, " '%s' not found\n", name_and_crc); + return 0; + } + fformat (vam->ofp, " '%s' has message index %d\n", + name_and_crc, message_index); + return 0; + } + errmsg ("name_and_crc required...\n"); + return 0; +} + static int search_node_table (vat_main_t * vam) { @@ -16971,6 +17032,8 @@ _(dump_ipv6_table, "usage: dump_ipv6_table") \ _(dump_stats_table, "usage: dump_stats_table") \ _(dump_macro_table, "usage: dump_macro_table ") \ _(dump_node_table, "usage: dump_node_table") \ +_(dump_msg_api_table, "usage: dump_msg_api_table") \ +_(get_msg_id, "usage: get_msg_id name_and_crc") \ _(echo, "usage: echo ") \ _(exec, "usage: exec ") \ _(exec_inband, "usage: exec_inband ") \ diff --git a/vpp-api/Makefile.am b/vpp-api/Makefile.am index 1812b63765c..310bd2321eb 100644 --- a/vpp-api/Makefile.am +++ b/vpp-api/Makefile.am @@ -1,2 +1,17 @@ AUTOMAKE_OPTIONS = foreign SUBDIRS = python java + +api_json_dir = $(abs_builddir)/vpp-api +api_srcs:=$(shell find $(prefix)/../ -name '*.api') +api_json:=$(patsubst %.api,$(api_json_dir)/%.api.json,$(notdir $(api_srcs))) + +define define_compile_rules +$(api_json_dir)/%.api.json: $(1)%.api + @echo " + Generating '$$<'" + @mkdir -p $$(@D) + $(CC) $$(CPPFLAGS) -E -P -C -x c $$< | vppapigen --input - --json $$@ +endef + +$(foreach directory,$(dir $(api_srcs)),$(eval $(call define_compile_rules,$(directory)))) + +BUILT_SOURCES = $(api_json) diff --git a/vpp-api/configure.ac b/vpp-api/configure.ac index 0a052cf9c5d..3d7bf251108 100644 --- a/vpp-api/configure.ac +++ b/vpp-api/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(vpp-api, 1.0.0) +AC_INIT(vpp-api, 1.1.0) LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SUBDIRS([java]) diff --git a/vpp-api/python/Makefile.am b/vpp-api/python/Makefile.am index 59b1b9220bd..d2c3fb5dd4e 100644 --- a/vpp-api/python/Makefile.am +++ b/vpp-api/python/Makefile.am @@ -52,7 +52,8 @@ install-exec-local: $(srcdir)/vpp_papi/vpe.py $(srcdir)/vpp_papi/memclnt.py cd $(srcdir); \ mkdir -p $(prefix)/lib/python2.7/site-packages; \ PYTHONUSERBASE=$(prefix) \ - python setup.py build_ext -L $(prefix)/lib64 install --user + python setup.py build_ext -L $(prefix)/lib64 \ + -I $(prefix)/../vppinfra/include/ install --user # # Test client diff --git a/vpp-api/python/pneum/api-gen.py b/vpp-api/python/pneum/api-gen.py deleted file mode 100755 index 445683523de..00000000000 --- a/vpp-api/python/pneum/api-gen.py +++ /dev/null @@ -1,351 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2016 Cisco and/or its affiliates. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import argparse, sys, os, importlib, pprint - -parser = argparse.ArgumentParser(description='VPP Python API generator') -parser.add_argument('-i', action="store", dest="inputfile") -parser.add_argument('-c', '--cfile', action="store") -args = parser.parse_args() - -sys.path.append(".") - -inputfile = args.inputfile.replace('.py', '') -cfg = importlib.import_module(inputfile, package=None) - -# https://docs.python.org/3/library/struct.html -format_struct = {'u8': 'B', - 'u16' : 'H', - 'u32' : 'I', - 'i32' : 'i', - 'u64' : 'Q', - 'f64' : 'd', - 'vl_api_fib_path_t' : 'IIBBBBBBBBBBBBBBBBBBBBB', - 'vl_api_ip4_fib_counter_t' : 'IBQQ', - 'vl_api_ip6_fib_counter_t' : 'QQBQQ', - 'vl_api_lisp_adjacency_t' : 'B' * 35, - }; -# -# NB: If new types are introduced in vpe.api, these must be updated. -# -type_size = {'u8': 1, - 'u16' : 2, - 'u32' : 4, - 'i32' : 4, - 'u64' : 8, - 'f64' : 8, - 'vl_api_fib_path_t' : 29, - 'vl_api_ip4_fib_counter_t' : 21, - 'vl_api_ip6_fib_counter_t' : 33, - 'vl_api_lisp_adjacency_t' : 35, -}; - -def get_args(t): - argslist = [] - for i in t: - if i[1][0] == '_': - argslist.append(i[1][1:]) - else: - argslist.append(i[1]) - - return argslist - -def get_pack(t): - zeroarray = False - bytecount = 0 - pack = '>' - tup = u'' - j = -1 - for i in t: - j += 1 - if len(i) is 3 or len(i) is 4: # TODO: add support for variable length arrays (VPP-162) - size = type_size[i[0]] - bytecount += size * int(i[2]) - # Check if we have a zero length array - if i[2] == '0': - tup += 'msg[' + str(bytecount) + ':],' - zeroarray = True - continue - if size == 1: - n = i[2] * size - pack += str(n) + 's' - tup += 'tr[' + str(j) + '],' - continue - pack += format_struct[i[0]] * int(i[2]) - tup += 'tr[' + str(j) + ':' + str(j + int(i[2])) + '],' - j += int(i[2]) - 1 - else: - bytecount += type_size[i[0]] - pack += format_struct[i[0]] - tup += 'tr[' + str(j) + '],' - return pack, bytecount, tup, zeroarray - -def get_reply_func(f): - if f['name']+'_reply' in func_name: - return func_name[f['name']+'_reply'] - if f['name'].find('_dump') > 0: - r = f['name'].replace('_dump','_details') - if r in func_name: - return func_name[r] - return None - -def get_enums(): - # Read enums from stdin - enums_by_name = {} - enums_by_index = {} - i = 1 - for l in sys.stdin: - l = l.replace(',\n','') - print l, "=", i - - l = l.replace('VL_API_','').lower() - enums_by_name[l] = i - enums_by_index[i] = l - - i += 1 - return enums_by_name, enums_by_index - -def get_definitions(): - # Pass 1 - func_list = [] - func_name = {} - i = 1 - for a in cfg.messages: - pack, packlen, tup, zeroarray = get_pack(a[1:]) - func_name[a[0]] = dict([('name', a[0]), ('pack', pack), ('packlen', packlen), ('tup', tup), ('args', get_args(a[1:])), - ('zeroarray', zeroarray)]) - func_list.append(func_name[a[0]]) # Indexed by name - return func_list, func_name - -def generate_c_macros(func_list, enums_by_name): - file = open(args.cfile, 'w+') - print >>file, "#define foreach_api_msg \\" - for f in func_list: - if not f['name'] in enums_by_name: - continue - print >>file, "_(" + f['name'].upper() + ", " + f['name'] + ") \\" - print >>file, ''' -void pneum_set_handlers(void) { -#define _(N,n) \\ - api_func_table[VL_API_##N] = sizeof(vl_api_##n##_t); - foreach_api_msg; -#undef _ -} - ''' - -# -# Print array with a hash of 'decode' and 'multipart' -# Simplify to do only decode for now. And deduce multipart from _dump? -# -def decode_function_print(name, args, pack, packlen, tup): - - print(u'def ' + name + u'_decode(msg):') - print(u" n = namedtuple('" + name + "', '" + ', '.join(args) + "')" + - ''' - if not n: - return None - ''') - print(u" tr = unpack('" + pack + "', msg[:" + str(packlen) + "])") - print(u" r = n._make((" + tup + "))" + - ''' - if not r: - return None - return r - ''') - -def function_print(name, id, args, pack, multipart, zeroarray): - if len(args) < 4: - print "def", name + "(async = False):" - else: - print "def", name + "(" + ', '.join(args[3:]) + ", async = False):" - print " global waiting_for_reply" - print " context = get_context(" + id + ")" - - print ''' - results[context] = {} - results[context]['e'] = threading.Event() - results[context]['e'].clear() - results[context]['r'] = [] - waiting_for_reply = True - ''' - if multipart == True: - print " results[context]['m'] = True" - - if zeroarray == True: - print " vpp_api.write(pack('" + pack + "', " + id + ", 0, context, " + ', '.join(args[3:-1]) + ") + " + args[-1] + ")" - else: - print " vpp_api.write(pack('" + pack + "', " + id + ", 0, context, " + ', '.join(args[3:]) + "))" - - if multipart == True: - print " vpp_api.write(pack('>HII', VL_API_CONTROL_PING, 0, context))" - - print ''' - if not async: - results[context]['e'].wait(5) - return results[context]['r'] - return context - ''' - -# -# Should dynamically create size -# -def api_table_print (name, msg_id): - f = name + '_decode' - print('api_func_table[' + msg_id + '] = ' + f) - -# -# Generate the main Python file -# - -print ''' - -# -# AUTO-GENERATED FILE. PLEASE DO NOT EDIT. -# -import sys, time, threading, signal, os, logging -from struct import * -from collections import namedtuple - -# -# Import C API shared object -# -import vpp_api - -context = 0 -results = {} -waiting_for_reply = False - -# -# XXX: Make this return a unique number -# -def get_context(id): - global context - context += 1 - return context - -def msg_handler(msg): - global result, context, event_callback, waiting_for_reply - if not msg: - logging.warning('vpp_api.read failed') - return - - id = unpack('>H', msg[0:2]) - logging.debug('Received message', id[0]) - if id[0] == VL_API_RX_THREAD_EXIT: - logging.info("We got told to leave") - return; - - # - # Decode message and returns a tuple. - # - logging.debug('api_func', api_func_table[id[0]]) - r = api_func_table[id[0]](msg) - if not r: - logging.warning('Message decode failed', id[0]) - return - - if 'context' in r._asdict(): - if r.context > 0: - context = r.context - - # - # XXX: Call provided callback for event - # Are we guaranteed to not get an event during processing of other messages? - # How to differentiate what's a callback message and what not? Context = 0? - # - logging.debug('R:', context, r, waiting_for_reply) - if waiting_for_reply == False: - event_callback(r) - return - - # - # Collect results until control ping - # - if id[0] == VL_API_CONTROL_PING_REPLY: - results[context]['e'].set() - waiting_for_reply = False - return - if not context in results: - logging.warning('Not expecting results for this context', context) - return - if 'm' in results[context]: - results[context]['r'].append(r) - return - - results[context]['r'] = r - results[context]['e'].set() - waiting_for_reply = False - -def connect(name): - signal.alarm(3) # 3 second - rv = vpp_api.connect(name, msg_handler) - signal.alarm(0) - logging.info("Connect:", rv) - return rv - -def disconnect(): - rv = vpp_api.disconnect() - logging.info("Disconnected") - return rv - -def register_event_callback(callback): - global event_callback - event_callback = callback -''' - -enums_by_name, enums_by_index = get_enums() -func_list, func_name = get_definitions() - -# -# Not needed with the new msg_size field. -# generate_c_macros(func_list, enums_by_name) -# - -pp = pprint.PrettyPrinter(indent=4) -#print 'enums_by_index =', pp.pprint(enums_by_index) -#print 'func_name =', pp.pprint(func_name) - -# Pass 2 - -# -# 1) The VPE API lacks a clear definition of what messages are reply messages -# 2) Length is missing, and has to be pre-known or in case of variable sized ones calculated per message type -# -for f in func_list: - #if f['name'].find('_reply') > 0 or f['name'].find('_details') > 0: - decode_function_print(f['name'], f['args'], f['pack'], f['packlen'], f['tup']) - - #r = get_reply_func(f) - #if not r: - # # - # # XXX: Functions here are not taken care of. E.g. events - # # - # print('Missing function', f) - # continue - - if f['name'].find('_dump') > 0: - f['multipart'] = True - else: - f['multipart'] = False - msg_id_in = 'VL_API_' + f['name'].upper() - function_print(f['name'], msg_id_in, f['args'], f['pack'], f['multipart'], f['zeroarray']) - - -print "api_func_table = [0] * 10000" -for f in func_list: - # if f['name'].find('_reply') > 0 or f['name'].find('_details') > 0: - msg_id_in = 'VL_API_' + f['name'].upper() - api_table_print(f['name'], msg_id_in) diff --git a/vpp-api/python/pneum/pneum.c b/vpp-api/python/pneum/pneum.c index ebe47b2f419..3b5455304e5 100644 --- a/vpp-api/python/pneum/pneum.c +++ b/vpp-api/python/pneum/pneum.c @@ -52,7 +52,7 @@ typedef struct { pneum_main_t pneum_main; -extern int wrap_pneum_callback(char *data, int len); +pneum_callback_t pneum_callback; /* * Satisfy external references when -lvlib is not available. @@ -62,24 +62,16 @@ void vlib_cli_output (struct vlib_main_t * vm, char * fmt, ...) clib_warning ("vlib_cli_output called..."); } -#define vl_api_version(n,v) static u32 vpe_api_version = v; -#include -#undef vl_api_version void -vl_client_add_api_signatures (vl_api_memclnt_create_t *mp) +pneum_free (void * msg) { - /* - * Send the main API signature in slot 0. This bit of code must - * match the checks in ../vpe/api/api.c: vl_msg_api_version_check(). - */ - mp->api_versions[0] = clib_host_to_net_u32 (vpe_api_version); + vl_msg_api_free (msg); } static void pneum_api_handler (void *msg) { u16 id = ntohs(*((u16 *)msg)); - if (id == VL_API_RX_THREAD_EXIT) { pneum_main_t *pm = &pneum_main; vl_msg_api_free(msg); @@ -91,8 +83,9 @@ pneum_api_handler (void *msg) clib_warning("Message ID %d has wrong length: %d\n", id, l); /* Call Python callback */ - (void)wrap_pneum_callback(msg, l); - vl_msg_api_free(msg); + ASSERT(pneum_callback); + (pneum_callback)(msg, l); + pneum_free(msg); } static void * @@ -115,8 +108,22 @@ pneum_rx_thread_fn (void *arg) pthread_exit(0); } +uword * +pneum_msg_table_get_hash (void) +{ + api_main_t *am = &api_main; + return (am->msg_index_by_name_and_crc); +} + int -pneum_connect (char * name, char * chroot_prefix) +pneum_msg_table_size(void) +{ + api_main_t *am = &api_main; + return hash_elts(am->msg_index_by_name_and_crc); +} + +int +pneum_connect (char * name, char * chroot_prefix, pneum_callback_t cb) { int rv = 0; pneum_main_t *pm = &pneum_main; @@ -134,12 +141,15 @@ pneum_connect (char * name, char * chroot_prefix) return (-1); } - /* Start the rx queue thread */ - rv = pthread_create(&pm->rx_thread_handle, NULL, pneum_rx_thread_fn, 0); - if (rv) { - clib_warning("pthread_create returned %d", rv); - vl_client_api_unmap(); - return (-1); + if (cb) { + /* Start the rx queue thread */ + rv = pthread_create(&pm->rx_thread_handle, NULL, pneum_rx_thread_fn, 0); + if (rv) { + clib_warning("pthread_create returned %d", rv); + vl_client_api_unmap(); + return (-1); + } + pneum_callback = cb; } pm->connected_to_vlib = 1; @@ -164,6 +174,7 @@ pneum_disconnect (void) if (pm->connected_to_vlib) { vl_client_disconnect(); vl_client_api_unmap(); + pneum_callback = 0; } memset (pm, 0, sizeof (*pm)); @@ -175,8 +186,11 @@ pneum_read (char **p, int *l) { unix_shared_memory_queue_t *q; api_main_t *am = &api_main; + pneum_main_t *pm = &pneum_main; uword msg; + if (!pm->connected_to_vlib) return -1; + *l = 0; if (am->our_pid == 0) return (-1); @@ -219,7 +233,9 @@ pneum_write (char *p, int l) api_main_t *am = &api_main; vl_api_header_t *mp = vl_msg_api_alloc(l); unix_shared_memory_queue_t *q; + pneum_main_t *pm = &pneum_main; + if (!pm->connected_to_vlib) return -1; if (!mp) return (-1); memcpy(mp, p, l); mp->client_index = pneum_client_index(); @@ -228,7 +244,7 @@ pneum_write (char *p, int l) if (rv != 0) { printf("vpe_api_write fails: %d\n", rv); /* Clear message */ - vl_msg_api_free(mp); + pneum_free(mp); } return (rv); } diff --git a/vpp-api/python/pneum/pneum.h b/vpp-api/python/pneum/pneum.h index 75b10f8df45..00585eb72e5 100644 --- a/vpp-api/python/pneum/pneum.h +++ b/vpp-api/python/pneum/pneum.h @@ -15,9 +15,15 @@ #ifndef included_pneum_h #define included_pneum_h -int pneum_connect(char * name, char * chroot_prefix); +#include + +typedef void (*pneum_callback_t)(unsigned char * data, int len); +int pneum_connect(char * name, char * chroot_prefix, pneum_callback_t cb); int pneum_disconnect(void); int pneum_read(char **data, int *l); int pneum_write(char *data, int len); +void pneum_free(void * msg); +uword * pneum_msg_table_get_hash (void); +int pneum_msg_table_size(void); #endif diff --git a/vpp-api/python/pneum/test_pneum.c b/vpp-api/python/pneum/test_pneum.c index 20c29a7e754..49107197f88 100644 --- a/vpp-api/python/pneum/test_pneum.c +++ b/vpp-api/python/pneum/test_pneum.c @@ -76,7 +76,7 @@ int main (int argc, char ** argv) vl_api_show_version_t message; vl_api_show_version_t *mp; int async = 1; - int rv = pneum_connect("pneum_client", NULL); + int rv = pneum_connect("pneum_client", NULL, NULL); if (rv != 0) { printf("Connect failed: %d\n", rv); diff --git a/vpp-api/python/vpp_papi/pneum_wrap.c b/vpp-api/python/vpp_papi/pneum_wrap.c index 18c4f233869..5763707b517 100644 --- a/vpp-api/python/vpp_papi/pneum_wrap.c +++ b/vpp-api/python/vpp_papi/pneum_wrap.c @@ -15,11 +15,12 @@ #include #include "../pneum/pneum.h" +#include static PyObject *pneum_callback = NULL; -int -wrap_pneum_callback (char *data, int len) +static void +wrap_pneum_callback (unsigned char * data, int len) { PyGILState_STATE gstate; PyObject *result;//, *arglist; @@ -38,7 +39,6 @@ wrap_pneum_callback (char *data, int len) PyErr_Print(); PyGILState_Release(gstate); - return (0); } static PyObject * @@ -46,22 +46,28 @@ wrap_connect (PyObject *self, PyObject *args) { char * name, * chroot_prefix = NULL; int rv; - PyObject * temp; + PyObject * temp = NULL; + pneum_callback_t cb = NULL; - if (!PyArg_ParseTuple(args, "sO|s:wrap_connect", &name, &temp, &chroot_prefix)) + if (!PyArg_ParseTuple(args, "s|Os:wrap_connect", + &name, &temp, &chroot_prefix)) return (NULL); - if (!PyCallable_Check(temp)) { - PyErr_SetString(PyExc_TypeError, "parameter must be callable"); - return NULL; - } - - Py_XINCREF(temp); /* Add a reference to new callback */ - Py_XDECREF(pneum_callback); /* Dispose of previous callback */ - pneum_callback = temp; /* Remember new callback */ - + if (temp) + { + if (!PyCallable_Check(temp)) + { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + + Py_XINCREF(temp); /* Add a reference to new callback */ + Py_XDECREF(pneum_callback); /* Dispose of previous callback */ + pneum_callback = temp; /* Remember new callback */ + cb = wrap_pneum_callback; + } Py_BEGIN_ALLOW_THREADS - rv = pneum_connect(name, chroot_prefix); + rv = pneum_connect(name, chroot_prefix, cb); Py_END_ALLOW_THREADS return PyLong_FromLong(rv); } @@ -90,8 +96,6 @@ wrap_write (PyObject *self, PyObject *args) return PyLong_FromLong(rv); } -void vl_msg_api_free(void *); - static PyObject * wrap_read (PyObject *self, PyObject *args) { @@ -110,15 +114,44 @@ wrap_read (PyObject *self, PyObject *args) #endif if (!ret) { Py_RETURN_NONE; } - vl_msg_api_free(data); + pneum_free(data); return ret; } +static PyObject * +wrap_msg_table (PyObject *self, PyObject *args) +{ + int i = 0, rv = 0; + hash_pair_t *hp; + uword *h = pneum_msg_table_get_hash(); + PyObject *ret = PyList_New(pneum_msg_table_size()); + if (!ret) goto error; + hash_foreach_pair (hp, h, + ({ + PyObject *item = PyTuple_New(2); + if (!item) goto error; + rv = PyTuple_SetItem(item, 0, PyLong_FromLong((u32)hp->value[0])); + if (rv) goto error; + rv = PyTuple_SetItem(item, 1, PyString_FromString((char *)hp->key)); + if (rv) goto error; + PyList_SetItem(ret, i, item); + i++; + })); + + return ret; + + error: + /* TODO: Raise exception */ + printf("msg_table failed"); + Py_RETURN_NONE; +} + static PyMethodDef vpp_api_Methods[] = { {"connect", wrap_connect, METH_VARARGS, "Connect to the VPP API."}, {"disconnect", wrap_disconnect, METH_VARARGS, "Disconnect from the VPP API."}, {"write", wrap_write, METH_VARARGS, "Write data to the VPP API."}, {"read", wrap_read, METH_VARARGS, "Read data from the VPP API."}, + {"msg_table", wrap_msg_table, METH_VARARGS, "Get API dictionary."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c index da2d3bb1a6e..ad5d43be079 100644 --- a/vpp/vpp-api/api.c +++ b/vpp/vpp-api/api.c @@ -9245,6 +9245,8 @@ static void vl_api_##nn##_t_handler ( \ vl_msg_api_free (mp); \ } +static void setup_message_id_table (api_main_t * am); + /* * vpe_api_hookup * Add vpe's API message handlers to the table. @@ -9252,7 +9254,6 @@ static void vl_api_##nn##_t_handler ( \ * added the client registration handlers. * See .../open-repo/vlib/memclnt_vlib.c:memclnt_process() */ - static clib_error_t * vpe_api_hookup (vlib_main_t * vm) { @@ -9306,6 +9307,11 @@ vpe_api_hookup (vlib_main_t * vm) am->is_mp_safe[VL_API_IP_ADD_DEL_ROUTE] = 1; am->is_mp_safe[VL_API_GET_NODE_GRAPH] = 1; + /* + * Set up the (msg_name, crc, message-id) table + */ + setup_message_id_table (am); + return 0; } @@ -9459,24 +9465,6 @@ get_unformat_vnet_sw_interface (void) return (void *) &unformat_vnet_sw_interface; } -#undef vl_api_version -#define vl_api_version(n,v) static u32 vpe_api_version = v; -#include -#undef vl_api_version - -int -vl_msg_api_version_check (vl_api_memclnt_create_t * mp) -{ - if (clib_host_to_net_u32 (mp->api_versions[0]) != vpe_api_version) - { - clib_warning ("vpe API mismatch: 0x%08x instead of 0x%08x", - clib_host_to_net_u32 (mp->api_versions[0]), - vpe_api_version); - return -1; - } - return 0; -} - static u8 * format_arp_event (u8 * s, va_list * args) { @@ -9540,6 +9528,20 @@ VLIB_CLI_COMMAND (show_ip_arp_nd_events, static) = { }; /* *INDENT-ON* */ +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (api_main_t * am) +{ +#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); + foreach_vl_msg_name_crc_memclnt; + foreach_vl_msg_name_crc_vpe; +#undef _ +} + + /* * fd.io coding-style-patch-verification: ON * diff --git a/vppapigen/lex.c b/vppapigen/lex.c index b011044dd01..e807d46b9a0 100644 --- a/vppapigen/lex.c +++ b/vppapigen/lex.c @@ -28,7 +28,7 @@ #include "node.h" #include "gram.h" -FILE *ifp, *ofp, *pythonfp; +FILE *ifp, *ofp, *pythonfp, *jsonfp; char *vlib_app_name = "vpp"; int dump_tree; time_t starttime; @@ -36,6 +36,7 @@ char *input_filename; char *current_filename; int current_filename_allocated; unsigned long input_crc; +unsigned long message_crc; int yydebug; /* @@ -258,6 +259,7 @@ int main (int argc, char **argv) int curarg = 1; char *ofile=0; char *pythonfile=0; + char *jsonfile=0; char *show_name=0; while (curarg < argc) { @@ -349,6 +351,27 @@ int main (int argc, char **argv) } continue; } + if (!strncmp (argv [curarg], "--json", 6)) { + curarg++; + if (curarg < argc) { + if (!strcmp(argv[curarg], "-")) { + jsonfp = stdout; + } else { + jsonfp = fopen(argv[curarg], "w"); + jsonfile = argv[curarg]; + } + if (jsonfp == NULL) { + fprintf (stderr, "Couldn't open JSON output file %s\n", + argv[curarg]); + exit (1); + } + curarg++; + } else { + fprintf(stderr, "Missing filename after --json\n"); + exit(1); + } + continue; + } if (!strncmp (argv [curarg], "--app", 4)) { curarg++; if (curarg < argc) { @@ -370,6 +393,9 @@ int main (int argc, char **argv) if (pythonfp == NULL) { pythonfile = 0; } + if (jsonfp == NULL) { + jsonfile = 0; + } if (ifp == NULL) { fprintf(stderr, "No input file specified...\n"); exit(1); @@ -391,6 +417,10 @@ int main (int argc, char **argv) printf ("Python bindings written to %s\n", pythonfile); fclose (pythonfp); } + if (jsonfile) { + printf ("JSON bindings written to %s\n", jsonfile); + fclose (jsonfp); + } } else { fclose (ifp); @@ -404,6 +434,10 @@ int main (int argc, char **argv) printf ("Removing %s\n", pythonfile); unlink (pythonfile); } + if (jsonfile) { + printf ("Removing %s\n", jsonfile); + unlink (jsonfile); + } exit (1); } exit (0); @@ -415,7 +449,8 @@ int main (int argc, char **argv) static void usage (char *progname) { fprintf (stderr, - "usage: %s --input [--output ] [--python ]\n%s", + "usage: %s --input [--output ] " + "[--json ] [--python ]\n%s", progname, " [--yydebug] [--dump-tree]\n"); exit (1); @@ -818,18 +853,18 @@ int yylex (void) */ unsigned long crc = input_crc; int node_type = yylex_1 (); + unsigned long crc2 = message_crc; + int use_helper_string = 0; + unsigned short code; switch (node_type) { case PRIMTYPE: case NAME: case NUMBER: case STRING: - case HELPER_STRING: { - /* We know these types accumulated token text into namebuf */ - /* HELPER_STRING may still contain C comments. Argh. */ - crc = crc_eliding_c_comments (namebuf, crc); + case HELPER_STRING: + use_helper_string = 1; break; - } /* Other node types have no "substate" */ /* This code is written in this curious fashion because we @@ -837,30 +872,25 @@ int yylex (void) * values a particular version of lex/bison assigned to various states. */ - /* case NAME: crc = CRC16 (crc, 257); break; */ - case RPAR: crc = CRC16 (crc, 258); break; - case LPAR: crc = CRC16 (crc, 259); break; - case SEMI: crc = CRC16 (crc, 260); break; - case LBRACK: crc = CRC16 (crc, 261); break; - case RBRACK: crc = CRC16 (crc, 262); break; - /* case NUMBER: crc = CRC16 (crc, 263); break; */ - /* case PRIMTYPE: crc = CRC16 (crc, 264); break; */ - case BARF: crc = CRC16 (crc, 265); break; - case TPACKED: crc = CRC16 (crc, 266); break; - case DEFINE: crc = CRC16 (crc, 267); break; - case LCURLY: crc = CRC16 (crc, 268); break; - case RCURLY: crc = CRC16 (crc, 269); break; - /* case STRING: crc = CRC16 (crc, 270); break; */ - case UNION: crc = CRC16 (crc, 271); break; - /* case HELPER_STRING: crc = CRC16 (crc, 272); break; */ - case COMMA: crc = CRC16 (crc, 273); break; - case NOVERSION: crc = CRC16 (crc, 274); break; - case MANUAL_PRINT: crc = CRC16 (crc, 275); break; - case MANUAL_ENDIAN: crc = CRC16 (crc, 276); break; - case TYPEONLY: crc = CRC16 (crc, 278); break; - case DONT_TRACE: crc = CRC16 (crc, 279); break; + case RPAR: code = 258; break; + case LPAR: code = 259; break; + case SEMI: code = 260; break; + case LBRACK: code = 261; break; + case RBRACK: code = 262; break; + case BARF: code = 265; break; + case TPACKED: code = 266; break; + case DEFINE: code = 267; break; + case LCURLY: code = 268; break; + case RCURLY: code = 269; break; + case UNION: code = 271; break; + case COMMA: code = 273; break; + case NOVERSION: code = 274; break; + case MANUAL_PRINT: code = 275; break; + case MANUAL_ENDIAN: code = 276; break; + case TYPEONLY: code = 278; break; + case DONT_TRACE: code = 279; break; - case EOF: crc = CRC16 (crc, ~0); break; /* hysterical compatibility */ + case EOF: code = ~0; break; /* hysterical compatibility */ default: fprintf(stderr, "yylex: node_type %d missing state CRC cookie\n", @@ -868,11 +898,23 @@ int yylex (void) exit(1); } + if (use_helper_string) + { + /* We know these types accumulated token text into namebuf */ + /* HELPER_STRING may still contain C comments. Argh. */ + crc = crc_eliding_c_comments (namebuf, crc); + crc2 = crc_eliding_c_comments (namebuf, crc2); + } else + { + crc = CRC16 (crc, code); + crc2 = CRC16 (crc2, code); + } + input_crc = crc; + message_crc = crc2; return (node_type); } - /* * name_check -- see if the name we just ate * matches a known keyword. If so, set yylval @@ -943,6 +985,7 @@ static int name_check (const char *s, YYSTYPE *token_value) return (TPACKED); case NODE_DEFINE: + message_crc = 0; *token_value = make_node(subclass_id); return(DEFINE); diff --git a/vppapigen/lex.h b/vppapigen/lex.h index 5bf38fe8093..e9b0954c0b9 100644 --- a/vppapigen/lex.h +++ b/vppapigen/lex.h @@ -45,5 +45,6 @@ enum lex_state { #define MAXNAME 64000 extern unsigned long input_crc; +extern unsigned long message_crc; #endif /* _LEX_H_ */ diff --git a/vppapigen/node.c b/vppapigen/node.c index fa7146ae497..4460fd5f933 100644 --- a/vppapigen/node.c +++ b/vppapigen/node.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -33,6 +34,7 @@ FILE *ofp; FILE *pythonfp; +FILE *jsonfp; time_t starttime; char *vlib_app_name; char *input_filename; @@ -130,6 +132,12 @@ void primtype_recursive_generate(node_t *this, enum passid which, FILE *ofp, fputs("', ", pythonfp); break; + case JSON_PASS: + fputs("[\"", jsonfp); + fputs((char *)type_name, jsonfp); + fputs("\", ", jsonfp); + break; + default: fprintf(stderr, "primtype_recursive_generate: unimp pass %d\n", which); break; @@ -377,6 +385,24 @@ void node_define_generate (node_t *this, enum passid which, FILE *fp) fprintf(fp, "),\n\n"); break; + case JSON_PASS: + fprintf(fp, "[\"%s\",\n", CDATA0); + child = this->deeper; + indent += 4; + while (child) { + node_vft_t *vftp = the_vft[child->type]; + indent_me(fp); + vftp->generate(child, which, fp); + child = child->peer; + fprintf(fp, ",\n"); + } + indent_me(fp); + fprintf (fp, "{\"crc\" : \"0x%08x\"}\n", (u32)(u64)CDATA3); + indent -= 4; + indent_me(fp); + fprintf(fp, "]"); + break; + default: fprintf(stderr, "node_define_generate: unimp pass %d\n", which); break; @@ -537,6 +563,10 @@ void node_scalar_generate (node_t *this, enum passid which, FILE *fp) fprintf(fp, "'%s'),\n", CDATA0); break; + case JSON_PASS: + fprintf(fp, "\"%s\"]", CDATA0); + break; + default: fprintf(stderr, "node_scalar_generate: unimp pass %d\n", which); } @@ -658,6 +688,14 @@ void node_vector_generate (node_t *this, enum passid which, FILE *fp) } break; + case JSON_PASS: + if (CDATA2 != 0) { /* variable length vector */ + fprintf(fp, "\"%s\", %d, \"%s\"]", CDATA0, IDATA1, CDATA2); + } else { + fprintf(fp, "\"%s\", %d]", CDATA0, IDATA1); + } + break; + default: fprintf(stderr, "node_vector_generate: unimp pass %d\n", which); } @@ -746,6 +784,15 @@ void node_complex_generate (node_t *this, enum passid which, FILE *fp) } break; + case JSON_PASS: + fprintf(fp, "[\"%s\", ", CDATA0); + deeper = this->deeper; + if (deeper) { + vftp = the_vft[deeper->type]; + vftp->generate(deeper, which, fp); + } + break; + default: fprintf(stderr, "node_complex_generate unimp pass %d...\n", which); break; @@ -879,6 +926,7 @@ YYSTYPE add_define (YYSTYPE a1, YYSTYPE a2) np = make_node(NODE_DEFINE); np->data[0] = a1; + np->data[3] = (void *) message_crc; deeper((YYSTYPE)np, a2); return ((YYSTYPE) np); } @@ -1065,15 +1113,11 @@ void generate_top_boilerplate(FILE *fp) fprintf (fp, " * Input file: %s\n", input_filename); fprintf (fp, " * Automatically generated: please edit the input file "); fprintf (fp, "NOT this file!\n"); - - /* Moron Acme trigger workaround */ - fprintf (fp, " * %syright (c) %s by Cisco Systems, Inc.\n", "Cop", - &datestring[20]); fprintf (fp, " */\n\n"); fprintf (fp, "#if defined(vl_msg_id)||defined(vl_union_id)||"); fprintf (fp, "defined(vl_printfun) \\\n ||defined(vl_endianfun)||"); fprintf (fp, " defined(vl_api_version)||defined(vl_typedefs) \\\n"); - fprintf (fp, " ||defined(vl_msg_name)\n"); + fprintf (fp, " ||defined(vl_msg_name)||defined(vl_msg_name_crc_list)\n"); fprintf (fp, "/* ok, something was selected */\n"); fprintf (fp, "#else\n"); fprintf (fp, "#warning no content included from %s\n", input_filename); @@ -1141,6 +1185,37 @@ void generate_msg_names(YYSTYPE a1, FILE *fp) fprintf (fp, "#endif\n\n"); } +void generate_msg_name_crc_list (YYSTYPE a1, FILE *fp) +{ + node_t *np = (node_t *)a1; + char *unique_suffix, *cp; + + unique_suffix = sxerox(fixed_name); + + cp = unique_suffix; + while (*cp && (*cp != '.')) + cp++; + if (*cp == '.') + *cp = 0; + + fprintf (fp, "\n/****** Message name, crc list ******/\n\n"); + + fprintf (fp, "#ifdef vl_msg_name_crc_list\n"); + fprintf (fp, "#define foreach_vl_msg_name_crc_%s ", unique_suffix); + + while (np) { + if (np->type == NODE_DEFINE) { + if (!(np->flags & NODE_FLAG_TYPEONLY)) { + fprintf (fp, "\\\n_(VL_API_%s, %s, %08x) ", + uppercase (np->data[0]), (i8 *) np->data[0], + (u32)(u64)np->data[3]); + } + } + np = np->peer; + } + fprintf (fp, "\n#endif\n\n"); +} + void generate_typedefs(YYSTYPE a1, FILE *fp) { node_t *np = (node_t *)a1; @@ -1341,6 +1416,47 @@ void generate_python_msg_definitions(YYSTYPE a1, FILE *fp) fprintf (fp, "\n]\n"); } +static bool +is_typeonly_check(node_t *np, bool typeonly) +{ + bool is_typeonly = (np->flags & NODE_FLAG_TYPEONLY); + return (is_typeonly == typeonly); +} + +static void +generate_json_definitions(YYSTYPE a1, FILE *fp, bool typeonly) +{ + node_t *np = (node_t *)a1; + node_vft_t *vftp; + indent_me(fp); + if (typeonly) + fprintf (fp, "\"types\" : [\n"); + else + fprintf (fp, "\"messages\" : [\n"); + + /* Walk the top-level node-list */ + bool comma = false; + indent += 4; + while (np) { + if (np->type == NODE_DEFINE && is_typeonly_check(np, typeonly)) { + /* Yeah, this is pedantic */ + vftp = the_vft[np->type]; + indent_me(fp); + vftp->generate(np, JSON_PASS, fp); + comma = true; + } + np = np->peer; + if (comma && np && + np->type == NODE_DEFINE && is_typeonly_check(np, typeonly)) + fprintf (fp, ",\n"); + + } + indent -= 4; + fprintf (fp, "\n"); + indent_me(fp); + fprintf(fp, "]"); +} + void generate_python_typeonly_definitions(YYSTYPE a1, FILE *fp) { node_t *np = (node_t *)a1; @@ -1368,6 +1484,22 @@ void generate_python(YYSTYPE a1, FILE *fp) fprintf (fp, "vl_api_version = 0x%08x\n\n", (unsigned int)input_crc); } +void generate_json(YYSTYPE a1, FILE *fp) +{ + fprintf (fp, "{\n"); + indent += 4; + generate_json_definitions(a1, fp, true); + fprintf (fp, ",\n"); + generate_json_definitions(a1, fp, false); + + /* + * API CRC signature + */ + fprintf (fp, ",\n\"vl_api_version\" :\"0x%08x\"\n", + (unsigned int)input_crc); + fprintf (fp, "}\n"); +} + void generate(YYSTYPE a1) { if (dump_tree) { @@ -1381,6 +1513,7 @@ void generate(YYSTYPE a1) generate_msg_ids(a1, ofp); generate_msg_names(a1, ofp); + generate_msg_name_crc_list(a1, ofp); generate_typedefs(a1, ofp); generate_uniondefs(a1, ofp); generate_printfun(a1, ofp); @@ -1391,4 +1524,7 @@ void generate(YYSTYPE a1) if (pythonfp) { generate_python(a1, pythonfp); } + if (jsonfp) { + generate_json(a1, jsonfp); + } } diff --git a/vppapigen/node.h b/vppapigen/node.h index f80985e1009..297d603615b 100644 --- a/vppapigen/node.h +++ b/vppapigen/node.h @@ -61,6 +61,7 @@ enum passid { ENDIANFUN_PASS, PRINTFUN_PASS, PYTHON_PASS, + JSON_PASS, }; extern void *make_node (enum node_subclass type); @@ -70,13 +71,14 @@ typedef struct node_ { struct node_ *peer; struct node_ *deeper; int flags; - void *data[3]; + void *data[4]; } node_t; /* To shut up gcc-4.2.x warnings */ #define CDATA0 ((char *)(this->data[0])) #define IDATA1 ((int)(uword)(this->data[1])) #define CDATA2 ((char *)(this->data[2])) +#define CDATA3 ((char *)(this->data[3])) #define NODE_FLAG_MANUAL_PRINT (1<<0) #define NODE_FLAG_MANUAL_ENDIAN (1<<1)