Add in-message cli_request/cli_reply API

This new CLI API is meant to replace the
cli_request/cli_reply that uses shared memory.

PS: checkstyle -- *hate*

Change-Id: I6318f8f6b9be2c2398b49dac9e2193c1998ea724
Signed-off-by: Ole Troan <ot@cisco.com>
diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c
index a87828f..b9cfd75 100644
--- a/vpp-api-test/vat/api_format.c
+++ b/vpp-api-test/vat/api_format.c
@@ -893,6 +893,34 @@
   vam->result_ready = 1;
 }
 
+static void
+vl_api_cli_inband_reply_t_handler (vl_api_cli_inband_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  i32 retval = ntohl (mp->retval);
+
+  vam->retval = retval;
+  vam->cmd_reply = mp->reply;
+  vam->result_ready = 1;
+}
+
+static void
+vl_api_cli_inband_reply_t_handler_json (vl_api_cli_inband_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t node;
+
+  vat_json_init_object (&node);
+  vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+  vat_json_object_add_string_copy (&node, "reply", mp->reply);
+
+  vat_json_print (vam->ofp, &node);
+  vat_json_free (&node);
+
+  vam->retval = ntohl (mp->retval);
+  vam->result_ready = 1;
+}
+
 static void vl_api_classify_add_del_table_reply_t_handler
   (vl_api_classify_add_del_table_reply_t * mp)
 {
@@ -3448,6 +3476,7 @@
 _(CONTROL_PING_REPLY, control_ping_reply)                               \
 _(NOPRINT_CONTROL_PING_REPLY, noprint_control_ping_reply)               \
 _(CLI_REPLY, cli_reply)                                                 \
+_(CLI_INBAND_REPLY, cli_inband_reply)                                   \
 _(SW_INTERFACE_ADD_DEL_ADDRESS_REPLY,                                   \
   sw_interface_add_del_address_reply)                                   \
 _(SW_INTERFACE_SET_TABLE_REPLY, sw_interface_set_table_reply) 		\
@@ -4087,6 +4116,45 @@
   return -99;
 }
 
+/*
+ * Future replacement of exec() that passes CLI buffers directly in
+ * the API messages instead of an additional shared memory area.
+ */
+static int
+exec_inband (vat_main_t * vam)
+{
+  vl_api_cli_inband_t *mp;
+  f64 timeout;
+  unformat_input_t *i = vam->input;
+
+  if (vec_len (i->buffer) == 0)
+    return -1;
+
+  if (vam->exec_mode == 0 && unformat (i, "mode"))
+    {
+      vam->exec_mode = 1;
+      return 0;
+    }
+  if (vam->exec_mode == 1 && (unformat (i, "exit") || unformat (i, "quit")))
+    {
+      vam->exec_mode = 0;
+      return 0;
+    }
+
+  /*
+   * In order for the CLI command to work, it
+   * must be a vector ending in \n, not a C-string ending
+   * in \n\0.
+   */
+  u32 len = vec_len (vam->input->buffer);
+  M2 (CLI_INBAND, cli_inband, len);
+  clib_memcpy (mp->cmd, vam->input->buffer, len);
+  mp->length = htonl (len);
+
+  S;
+  W2 (fformat (vam->ofp, "%s", vam->cmd_reply));
+}
+
 static int
 api_create_loopback (vat_main_t * vam)
 {
@@ -15893,6 +15961,7 @@
 _(dump_node_table, "usage: dump_node_table")			\
 _(echo, "usage: echo <message>")				\
 _(exec, "usage: exec <vpe-debug-CLI-command>")                  \
+_(exec_inband, "usage: exec_inband <vpe-debug-CLI-command>")    \
 _(help, "usage: help")                                          \
 _(q, "usage: quit")                                             \
 _(quit, "usage: quit")                                          \
diff --git a/vpp-api-test/vat/vat.h b/vpp-api-test/vat/vat.h
index 311b9c7..ce8b166 100644
--- a/vpp-api-test/vat/vat.h
+++ b/vpp-api-test/vat/vat.h
@@ -166,6 +166,7 @@
   volatile i32 retval;
   volatile u32 sw_if_index;
   volatile u8 *shmem_result;
+  volatile u8 *cmd_reply;
 
   /* our client index */
   u32 my_client_index;
diff --git a/vpp-api/python/tests/test_cli.py b/vpp-api/python/tests/test_cli.py
new file mode 100755
index 0000000..66fb694
--- /dev/null
+++ b/vpp-api/python/tests/test_cli.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import unittest, sys, time, threading, struct
+import test_base
+import vpp_papi
+from ipaddress import *
+
+import glob, subprocess
+class TestPAPI(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        #
+        # Start main VPP process
+        cls.vpp_bin = glob.glob(test_base.scriptdir+'/../../../build-root/install-vpp*-native/vpp/bin/vpp')[0]
+        print("VPP BIN:", cls.vpp_bin)
+        cls.vpp = subprocess.Popen([cls.vpp_bin, "unix", "nodaemon"], stderr=subprocess.PIPE)
+        print('Started VPP')
+        # For some reason unless we let VPP start up the API cannot connect.
+        time.sleep(0.3)
+    @classmethod
+    def tearDownClass(cls):
+        cls.vpp.terminate()
+
+    def setUp(self):
+        print("Connecting API")
+        r = vpp_papi.connect("test_papi")
+        self.assertEqual(r, 0)
+
+    def tearDown(self):
+        r = vpp_papi.disconnect()
+        self.assertEqual(r, 0)
+
+    #
+    # The tests themselves
+    #
+
+    #
+    # Basic request / reply
+    #
+    def test_cli_request(self):
+        print(vpp_papi.cli_exec('show version verbose'))
+        #t = vpp_papi.cli_inband_request(len(cmd), cmd)
+        #print('T:',t)
+        #reply = t.reply[0].decode().rstrip('\x00')
+        #print(reply)
+        #program = t.program.decode().rstrip('\x00')
+        #self.assertEqual('vpe', program)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/vpp-api/python/vpp_papi/vpp_papi.py b/vpp-api/python/vpp_papi/vpp_papi.py
index 6a7a358..144151c 100644
--- a/vpp-api/python/vpp_papi/vpp_papi.py
+++ b/vpp-api/python/vpp_papi/vpp_papi.py
@@ -99,6 +99,12 @@
     logging.info("Disconnected")
     return rv
 
+# CLI convenience wrapper
+def cli_exec(cmd):
+    cmd += '\n'
+    r = cli_inband(len(cmd), cmd)
+    return r.reply[0].decode().rstrip('\x00')
+
 def register_event_callback(callback):
     event_callback_set(callback)
 
diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c
index fbebfa6..ead5f0c 100644
--- a/vpp/vpp-api/api.c
+++ b/vpp/vpp-api/api.c
@@ -155,6 +155,22 @@
     vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
 } while(0);
 
+#define REPLY_MACRO3(t, n, body)				\
+do {                                                            \
+    unix_shared_memory_queue_t * q;                             \
+    rv = vl_msg_api_pd_handler (mp, rv);                        \
+    q = vl_api_client_index_to_input_queue (mp->client_index);  \
+    if (!q)                                                     \
+        return;                                                 \
+                                                                \
+    rmp = vl_msg_api_alloc (sizeof (*rmp) + n);                 \
+    rmp->_vl_msg_id = ntohs((t));                               \
+    rmp->context = mp->context;                                 \
+    rmp->retval = ntohl(rv);                                    \
+    do {body;} while (0);                                       \
+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
+} while(0);
+
 #if (1 || CLIB_DEBUG > 0)	/* "trust, but verify" */
 
 #define VALIDATE_SW_IF_INDEX(mp)				\
@@ -268,6 +284,7 @@
 _(CONTROL_PING, control_ping)                                           \
 _(NOPRINT_CONTROL_PING, noprint_control_ping)                           \
 _(CLI_REQUEST, cli_request)                                             \
+_(CLI_INBAND, cli_inband)						\
 _(SET_ARP_NEIGHBOR_LIMIT, set_arp_neighbor_limit)			\
 _(L2_PATCH_ADD_DEL, l2_patch_add_del)					\
 _(CLASSIFY_ADD_DEL_TABLE, classify_add_del_table)			\
@@ -3693,6 +3710,46 @@
 }
 
 static void
+inband_cli_output (uword arg, u8 * buffer, uword buffer_bytes)
+{
+  u8 **mem_vecp = (u8 **) arg;
+  u8 *mem_vec = *mem_vecp;
+  u32 offset = vec_len (mem_vec);
+
+  vec_validate (mem_vec, offset + buffer_bytes - 1);
+  clib_memcpy (mem_vec + offset, buffer, buffer_bytes);
+  *mem_vecp = mem_vec;
+}
+
+static void
+vl_api_cli_inband_t_handler (vl_api_cli_inband_t * mp)
+{
+  vl_api_cli_inband_reply_t *rmp;
+  int rv = 0;
+  unix_shared_memory_queue_t *q;
+  vlib_main_t *vm = vlib_get_main ();
+  unformat_input_t input;
+  u8 *out_vec = 0;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (!q)
+    return;
+
+  unformat_init_string (&input, (char *) mp->cmd, ntohl (mp->length));
+  vlib_cli_input (vm, &input, inband_cli_output, (uword) & out_vec);
+
+  u32 len = vec_len (out_vec);
+  /* *INDENT-OFF* */
+  REPLY_MACRO3(VL_API_CLI_INBAND_REPLY, len,
+  ({
+    rmp->length = htonl (len);
+    clib_memcpy (rmp->reply, out_vec, len);
+  }));
+  /* *INDENT-ON* */
+  vec_free (out_vec);
+}
+
+static void
 vl_api_set_arp_neighbor_limit_t_handler (vl_api_set_arp_neighbor_limit_t * mp)
 {
   int rv;
diff --git a/vpp/vpp-api/custom_dump.c b/vpp/vpp-api/custom_dump.c
index 566bcb9..5ca7ccc 100644
--- a/vpp/vpp-api/custom_dump.c
+++ b/vpp/vpp-api/custom_dump.c
@@ -1621,6 +1621,16 @@
   FINISH;
 }
 
+static void *vl_api_cli_inband_t_print
+  (vl_api_cli_inband_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: cli_inband ");
+
+  FINISH;
+}
+
 static void *vl_api_memclnt_create_t_print
   (vl_api_memclnt_create_t * mp, void *handle)
 {
@@ -2695,6 +2705,7 @@
 _(CONTROL_PING, control_ping)						\
 _(WANT_INTERFACE_EVENTS, want_interface_events)				\
 _(CLI_REQUEST, cli_request)						\
+_(CLI_INBAND, cli_inband)						\
 _(MEMCLNT_CREATE, memclnt_create)					\
 _(SW_INTERFACE_VHOST_USER_DUMP, sw_interface_vhost_user_dump)           \
 _(SHOW_VERSION, show_version)                                           \
diff --git a/vpp/vpp-api/vpe.api b/vpp/vpp-api/vpe.api
index 1f26d55..51862f7 100644
--- a/vpp/vpp-api/vpe.api
+++ b/vpp/vpp-api/vpe.api
@@ -1183,6 +1183,13 @@
   u32 context;
   u64 cmd_in_shmem;
 };
+define cli_inband
+{
+  u32 client_index;
+  u32 context;
+  u32 length;
+  u8 cmd[length];
+};
 
 /** \brief vpe parser cli string response
     @param context - sender context, to match reply w/ request
@@ -1195,6 +1202,13 @@
   i32 retval;
   u64 reply_in_shmem;
 };
+define cli_inband_reply
+{
+  u32 context;
+  i32 retval;
+  u32 length;
+  u8 reply[length];
+};
 
 /** \brief Set max allowed ARP or ip6 neighbor entries request
     @param client_index - opaque cookie to identify the sender