Add client-side msg_name_and_crc -> msg_index table 70/3770/12
authorDave Barach <dave@barachs.net>
Thu, 10 Nov 2016 19:22:49 +0000 (14:22 -0500)
committerDamjan Marion <dmarion.lists@gmail.com>
Mon, 21 Nov 2016 18:11:41 +0000 (18:11 +0000)
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 <expected crc> appended.

Try these vpp-api-test commands to play with it:

vat# dump_msg_api_table
     <snip>
 [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 <dave@barachs.net>
Signed-off-by: Ole Troan <ot@cisco.com>
28 files changed:
build-root/emacs-lisp/plugin-main-skel.el
plugins/flowperpkt-plugin/flowperpkt/flowperpkt.c
plugins/ioam-plugin/ioam/export/ioam_export.c
plugins/ioam-plugin/ioam/lib-pot/pot_api.c
plugins/ioam-plugin/ioam/lib-trace/trace_api.c
plugins/lb-plugin/lb/api.c
plugins/sample-plugin/sample/sample.c
plugins/snat-plugin/Makefile.am
plugins/snat-plugin/snat/snat.c
vlib-api/vlibapi/api.h
vlib-api/vlibapi/api_shared.c
vlib-api/vlibmemory/memclnt.api
vlib-api/vlibmemory/memory_shared.c
vlib-api/vlibmemory/memory_vlib.c
vpp-api-test/vat/api_format.c
vpp-api/Makefile.am
vpp-api/configure.ac
vpp-api/python/Makefile.am
vpp-api/python/pneum/api-gen.py [deleted file]
vpp-api/python/pneum/pneum.c
vpp-api/python/pneum/pneum.h
vpp-api/python/pneum/test_pneum.c
vpp-api/python/vpp_papi/pneum_wrap.c
vpp/vpp-api/api.c
vppapigen/lex.c
vppapigen/lex.h
vppapigen/node.c
vppapigen/node.h

index 196f865..4724069 100644 (file)
@@ -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) <current-year> <your-organization>
  * 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 <interface-name> [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 "\",
index dbdb189..529b919 100644 (file)
@@ -438,6 +438,19 @@ flowperpkt_plugin_api_hookup (vlib_main_t * vm)
   return 0;
 }
 
+#define vl_msg_name_crc_list
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#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 */
index 9efcab0..e51c43e 100644 (file)
@@ -176,6 +176,19 @@ ioam_export_plugin_api_hookup (vlib_main_t * vm)
   return 0;
 }
 
+#define vl_msg_name_crc_list
+#include <ioam/export/ioam_export_all_api_h.h>
+#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);
index 95be9e0..484f223 100644 (file)
@@ -206,6 +206,19 @@ pot_plugin_api_hookup (vlib_main_t *vm)
     return 0;
 }
 
+#define vl_msg_name_crc_list
+#include <ioam/lib-pot/pot_all_api_h.h>
+#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;
index b98e65f..32c8e23 100644 (file)
@@ -178,6 +178,19 @@ trace_plugin_api_hookup (vlib_main_t * vm)
   return 0;
 }
 
+#define vl_msg_name_crc_list
+#include <ioam/lib-trace/trace_all_api_h.h>
+#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;
index 56806d2..06c53fa 100644 (file)
@@ -47,6 +47,19 @@ typedef enum {
 #include <lb/lb.api.h>
 #undef vl_api_version
 
+#define vl_msg_name_crc_list
+#include <lb/lb.api.h>
+#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;
 }
 
index 321b66c..603cb2d 100644 (file)
@@ -210,6 +210,19 @@ sample_plugin_api_hookup (vlib_main_t *vm)
     return 0;
 }
 
+#define vl_msg_name_crc_list
+#include <sample/sample_all_api_h.h>
+#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;
index 9f41193..0a0fff8 100644 (file)
@@ -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
index 66c0381..2956e24 100644 (file)
@@ -955,6 +955,19 @@ snat_plugin_api_hookup (vlib_main_t *vm)
     return 0;
 }
 
+#define vl_msg_name_crc_list
+#include <snat/snat_all_api_h.h>
+#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);
 
index a932bf4..10ca443 100644 (file)
@@ -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,
index 511f693..2b2d81c 100644 (file)
@@ -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
  *
index c275823..2f654ca 100644 (file)
@@ -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 */
 };
 
 /*
index 134fcd5..6adc69f 100644 (file)
@@ -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)
index ca3eb14..5f97f16 100644 (file)
@@ -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);
 }
index 3d7ce46..00bb976 100644 (file)
@@ -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 <message>")                               \
 _(exec, "usage: exec <vpe-debug-CLI-command>")                  \
 _(exec_inband, "usage: exec_inband <vpe-debug-CLI-command>")    \
index 1812b63..310bd23 100644 (file)
@@ -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)
index 0a052cf..3d7bf25 100644 (file)
@@ -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])
index 59b1b92..d2c3fb5 100644 (file)
@@ -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 (executable)
index 4456835..0000000
+++ /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)
index ebe47b2..3b54553 100644 (file)
@@ -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 <vpp-api/vpe.api.h>
-#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);
 }
index 75b10f8..00585eb 100644 (file)
 #ifndef included_pneum_h
 #define included_pneum_h
 
-int pneum_connect(char * name, char * chroot_prefix);
+#include <vppinfra/types.h>
+
+typedef void (*pneum_callback_t)(unsigned char * data, int len);
+int pneum_connect(char * name, char * chroot_prefix, pneum_callback_t cb);
 int 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
index 20c29a7..4910719 100644 (file)
@@ -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);
index 18c4f23..5763707 100644 (file)
 
 #include <Python.h>
 #include "../pneum/pneum.h"
+#include <vppinfra/hash.h>
 
 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 */
 };
 
index da2d3bb..ad5d43b 100644 (file)
@@ -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 <vpp-api/vpe.api.h>
-#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 <vpp-api/vpe_all_api_h.h>
+#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
  *
index b011044..e807d46 100644 (file)
@@ -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 <filename> [--output <filename>] [--python <filename>]\n%s",
+             "usage: %s --input <filename> [--output <filename>] "
+            "[--json <filename>] [--python <filename>]\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);
 
index 5bf38fe..e9b0954 100644 (file)
@@ -45,5 +45,6 @@ enum lex_state {
 #define MAXNAME 64000
 
 extern unsigned long input_crc;
+extern unsigned long message_crc;
 
 #endif /* _LEX_H_ */
index fa7146a..4460fd5 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <stdio.h>
+#include <stdbool.h>
 #include <ctype.h>
 #include <time.h>
 #include <string.h>
@@ -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);
+    }
 }
index f80985e..297d603 100644 (file)
@@ -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)