summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOle Troan <otroan@employees.org>2024-01-23 18:56:23 +0100
committerAndrew Yourtchenko <ayourtch@gmail.com>2024-01-29 22:57:31 +0000
commitac0babd412e3b5282136a5c5c5be2c4cc4be6895 (patch)
tree5c58249463d7121a163534e55b1d5f6f27ae7967
parentf34b6800de78d99192dc5757a4845e2668749c40 (diff)
api: provide api definition over api
This patch allows a client to bootstrap itself by downloading the JSON API definitions over the API itself. This patch enables it for Python (probably need a dynamic language). Call VPPApiClient with the new bootstrapapi=True parameter. Example (Python): from vpp_papi import VPPApiClient vpp = VPPApiClient(bootstrapapi=True) rv = vpp.connect("foobar") assert rv == 0 print(f'SHOW VERSION: {vpp.api.show_version()}') vpp.disconnect() Type: feature Change-Id: Id903fdccc82b2e22aa1994331d2c150253f2ccae Signed-off-by: Ole Troan <otroan@employees.org>
-rw-r--r--src/cmake/api.cmake2
-rwxr-xr-xsrc/tools/vppapigen/generate_json.py9
-rwxr-xr-xsrc/tools/vppapigen/vppapigen_c.py2
-rw-r--r--src/tools/vppapigen/vppapigen_json.py30
-rw-r--r--src/vlibapi/api_common.h2
-rw-r--r--src/vlibmemory/memclnt.api19
-rw-r--r--src/vlibmemory/memclnt_api.c36
-rw-r--r--src/vpp-api/python/setup.py4
-rw-r--r--src/vpp-api/python/vpp_papi/data/memclnt.api.json809
-rw-r--r--src/vpp-api/python/vpp_papi/vpp_papi.py84
10 files changed, 968 insertions, 29 deletions
diff --git a/src/cmake/api.cmake b/src/cmake/api.cmake
index 831c2b1e32d..0e273a7d4dc 100644
--- a/src/cmake/api.cmake
+++ b/src/cmake/api.cmake
@@ -64,7 +64,7 @@ function(vpp_generate_api_json_header file dir component)
add_custom_command (OUTPUT ${output_name}
COMMAND mkdir -p ${output_dir}
COMMAND ${PYENV} ${VPP_APIGEN}
- ARGS ${includedir} --includedir ${CMAKE_SOURCE_DIR} --input ${CMAKE_CURRENT_SOURCE_DIR}/${file} JSON --output ${output_name}
+ ARGS ${includedir} --includedir ${CMAKE_SOURCE_DIR} --input ${CMAKE_CURRENT_SOURCE_DIR}/${file} JSON --outputdir ${output_dir} --output ${output_name}
DEPENDS ${VPP_APIGEN} ${CMAKE_CURRENT_SOURCE_DIR}/${file}
COMMENT "Generating API header ${output_name}"
)
diff --git a/src/tools/vppapigen/generate_json.py b/src/tools/vppapigen/generate_json.py
index 610f84f5533..dc5cf9c1bbf 100755
--- a/src/tools/vppapigen/generate_json.py
+++ b/src/tools/vppapigen/generate_json.py
@@ -110,6 +110,15 @@ def main():
],
f.name,
),
+ "outputdir": "%s/%s/"
+ % (
+ output_path,
+ output_dir_map[
+ f.as_posix().split("/")[
+ src_dir_depth + BASE_DIR.count("/") - 1
+ ]
+ ],
+ ),
"input_file": f.as_posix(),
"includedir": [src_dir.as_posix()],
"output_module": "JSON",
diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py
index 64327887c0e..fb7de0a023f 100755
--- a/src/tools/vppapigen/vppapigen_c.py
+++ b/src/tools/vppapigen/vppapigen_c.py
@@ -1574,6 +1574,7 @@ def generate_c_boilerplate(services, defines, counters, file_crc, module, stream
#include "{module}.api.h"
#undef vl_printfun
+#include "{module}.api_json.h"
"""
write(hdr.format(module=module))
@@ -1586,6 +1587,7 @@ def generate_c_boilerplate(services, defines, counters, file_crc, module, stream
' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
"VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
)
+ write(f" vec_add1(am->json_api_repr, (u8 *)json_api_repr_{module});\n")
for d in defines:
write(
diff --git a/src/tools/vppapigen/vppapigen_json.py b/src/tools/vppapigen/vppapigen_json.py
index 0a1a3d37373..7239d1ea732 100644
--- a/src/tools/vppapigen/vppapigen_json.py
+++ b/src/tools/vppapigen/vppapigen_json.py
@@ -1,5 +1,7 @@
# JSON generation
import json
+import sys
+import os
process_imports = True
@@ -88,7 +90,26 @@ def walk_defs(s, is_message=False):
#
# Plugin entry point
#
-def run(output_dir, filename, s):
+
+
+def contents_to_c_string(contents):
+ # Escape backslashes and double quotes
+ contents = contents.replace("\\", "\\\\").replace('"', '\\"')
+ # Replace newlines with \n
+ contents = contents.replace("\n", "\\n")
+ return '"' + contents + '"'
+
+
+def run(output_dir, apifilename, s):
+ if not output_dir:
+ sys.stderr.write("Missing --outputdir argument")
+ return None
+
+ basename = os.path.basename(apifilename)
+ filename_json_repr = os.path.join(output_dir + "/" + basename + "_json.h")
+ filename, _ = os.path.splitext(basename)
+ modulename = filename.replace(".", "_")
+
j = {}
j["types"] = walk_defs([o for o in s["types"] if o.__class__.__name__ == "Typedef"])
@@ -106,4 +127,9 @@ def run(output_dir, filename, s):
j["vl_api_version"] = hex(s["file_crc"])
j["imports"] = walk_imports(i for i in s["Import"])
j["counters"], j["paths"] = walk_counters(s["Counters"], s["Paths"])
- return json.dumps(j, indent=4, separators=(",", ": "))
+ r = json.dumps(j, indent=4, separators=(",", ": "))
+ c_string = contents_to_c_string(r)
+ with open(filename_json_repr, "w", encoding="UTF-8") as f:
+ print(f"const char *json_api_repr_{modulename} = {c_string};", file=f)
+ # return json.dumps(j, indent=4, separators=(",", ": "))
+ return r
diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h
index 35d3b2a110e..62a8d4c62d8 100644
--- a/src/vlibapi/api_common.h
+++ b/src/vlibapi/api_common.h
@@ -354,6 +354,8 @@ typedef struct api_main_t
/** client message index hash table */
uword *msg_index_by_name_and_crc;
+ /** plugin JSON representation vector table */
+ u8 **json_api_repr;
/** api version list */
api_version_t *api_version_list;
diff --git a/src/vlibmemory/memclnt.api b/src/vlibmemory/memclnt.api
index a8e7cfeaec0..dc0f4e1c8a7 100644
--- a/src/vlibmemory/memclnt.api
+++ b/src/vlibmemory/memclnt.api
@@ -27,7 +27,7 @@ service {
};
/*
- * Create a client registration
+ * Create a client registration
*/
define memclnt_create {
option deprecated;
@@ -50,7 +50,7 @@ define memclnt_create_reply {
};
/*
- * Delete a client registration
+ * Delete a client registration
*/
define memclnt_delete {
u32 index; /* index, used e.g. by API trace replay */
@@ -155,7 +155,7 @@ typedef message_table_entry
};
/*
- * Create a socket client registration.
+ * Create a socket client registration.
*/
define sockclnt_create {
u32 context; /* opaque value to be returned in the reply */
@@ -172,7 +172,7 @@ define sockclnt_create_reply {
};
/*
- * Delete a client registration
+ * Delete a client registration
*/
define sockclnt_delete {
u32 client_index;
@@ -252,3 +252,14 @@ define memclnt_create_v2_reply {
u32 index; /* index, used e.g. by API trace replay */
u64 message_table; /* serialized message table in shmem */
};
+
+define get_api_json {
+ u32 client_index;
+ u32 context;
+};
+
+define get_api_json_reply {
+ u32 context;
+ i32 retval;
+ string json[];
+};
diff --git a/src/vlibmemory/memclnt_api.c b/src/vlibmemory/memclnt_api.c
index 7eb61fe7875..299e8d93f02 100644
--- a/src/vlibmemory/memclnt_api.c
+++ b/src/vlibmemory/memclnt_api.c
@@ -145,10 +145,44 @@ vl_api_control_ping_t_handler (vl_api_control_ping_t *mp)
({ rmp->vpe_pid = ntohl (getpid ()); }));
}
+static void
+vl_api_get_api_json_t_handler (vl_api_get_api_json_t *mp)
+{
+ vl_api_get_api_json_reply_t *rmp;
+ api_main_t *am = vlibapi_get_main ();
+ int rv = 0, n = 0;
+ u8 *s = 0;
+
+ vl_api_registration_t *rp =
+ vl_api_client_index_to_registration (mp->client_index);
+ if (rp == 0)
+ return;
+
+ s = format (s, "[\n");
+ u8 **ptr;
+ vec_foreach (ptr, am->json_api_repr)
+ {
+ s = format (s, "%s,", ptr[0]);
+ }
+ s[vec_len (s) - 1] = ']'; // Replace last comma with a bracket
+ vec_terminate_c_string (s);
+ n = vec_len (s);
+
+done:
+ REPLY_MACRO3 (VL_API_GET_API_JSON_REPLY, n, ({
+ if (rv == 0)
+ {
+ vl_api_c_string_to_api_string ((char *) s, &rmp->json);
+ }
+ }));
+ vec_free (s);
+}
+
#define foreach_vlib_api_msg \
_ (GET_FIRST_MSG_ID, get_first_msg_id) \
_ (API_VERSIONS, api_versions) \
- _ (CONTROL_PING, control_ping)
+ _ (CONTROL_PING, control_ping) \
+ _ (GET_API_JSON, get_api_json)
/*
* vl_api_init
diff --git a/src/vpp-api/python/setup.py b/src/vpp-api/python/setup.py
index 18637ba21b7..784013fc606 100644
--- a/src/vpp-api/python/setup.py
+++ b/src/vpp-api/python/setup.py
@@ -11,7 +11,6 @@
# 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 sys
try:
from setuptools import setup, find_packages
@@ -22,7 +21,7 @@ requirements = []
setup(
name="vpp_papi",
- version="2.0.0",
+ version="2.1.0",
description="VPP Python binding",
author="Ole Troan",
author_email="ot@cisco.com",
@@ -31,6 +30,7 @@ setup(
test_suite="vpp_papi.tests",
install_requires=requirements,
packages=find_packages(),
+ package_data={"vpp_papi": ["data/*.json"]},
long_description="""VPP Python language binding.""",
zip_safe=True,
)
diff --git a/src/vpp-api/python/vpp_papi/data/memclnt.api.json b/src/vpp-api/python/vpp_papi/data/memclnt.api.json
new file mode 100644
index 00000000000..1734cf12ab0
--- /dev/null
+++ b/src/vpp-api/python/vpp_papi/data/memclnt.api.json
@@ -0,0 +1,809 @@
+{
+ "types": [
+ [
+ "module_version",
+ [
+ "u32",
+ "major"
+ ],
+ [
+ "u32",
+ "minor"
+ ],
+ [
+ "u32",
+ "patch"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ]
+ ],
+ [
+ "message_table_entry",
+ [
+ "u16",
+ "index"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ]
+ ]
+ ],
+ "messages": [
+ [
+ "memclnt_create",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "ctx_quota"
+ ],
+ [
+ "u64",
+ "input_queue"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ [
+ "u32",
+ "api_versions",
+ 8
+ ],
+ {
+ "crc": "0x9c5e1c2f",
+ "options": {
+ "deprecated": null
+ },
+ "comment": "/*\n * Create a client registration\n */"
+ }
+ ],
+ [
+ "memclnt_create_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u64",
+ "message_table"
+ ],
+ {
+ "crc": "0x42ec4560",
+ "options": {
+ "deprecated": null
+ }
+ }
+ ],
+ [
+ "memclnt_delete",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ [
+ "bool",
+ "do_cleanup"
+ ],
+ {
+ "crc": "0x7e1c04e3",
+ "options": {},
+ "comment": "/*\n * Delete a client registration\n */"
+ }
+ ],
+ [
+ "memclnt_delete_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ {
+ "crc": "0x3d3b6312",
+ "options": {}
+ }
+ ],
+ [
+ "rx_thread_exit",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u8",
+ "dummy"
+ ],
+ {
+ "crc": "0xc3a3a452",
+ "options": {},
+ "comment": "/*\n * Client RX thread exit\n */"
+ }
+ ],
+ [
+ "memclnt_rx_thread_suspend",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u8",
+ "dummy"
+ ],
+ {
+ "crc": "0xc3a3a452",
+ "options": {},
+ "comment": "/*\n * Client RX thread suspend\n */"
+ }
+ ],
+ [
+ "memclnt_read_timeout",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u8",
+ "dummy"
+ ],
+ {
+ "crc": "0xc3a3a452",
+ "options": {},
+ "comment": "/*\n * Client read timeout\n */"
+ }
+ ],
+ [
+ "rpc_call",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u64",
+ "function"
+ ],
+ [
+ "u8",
+ "multicast"
+ ],
+ [
+ "u8",
+ "need_barrier_sync"
+ ],
+ [
+ "u8",
+ "send_reply"
+ ],
+ [
+ "u32",
+ "data_len"
+ ],
+ [
+ "u8",
+ "data",
+ 0,
+ "data_len"
+ ],
+ {
+ "crc": "0x7e8a2c95",
+ "options": {},
+ "comment": "/*\n * RPC\n */"
+ }
+ ],
+ [
+ "rpc_call_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804",
+ "options": {}
+ }
+ ],
+ [
+ "get_first_msg_id",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ {
+ "crc": "0xebf79a66",
+ "options": {},
+ "comment": "/*\n * Lookup message-ID base by name\n */"
+ }
+ ],
+ [
+ "get_first_msg_id_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u16",
+ "first_msg_id"
+ ],
+ {
+ "crc": "0x7d337472",
+ "options": {}
+ }
+ ],
+ [
+ "api_versions",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {},
+ "comment": "/*\n * Get API version table (includes built-in and plugins)\n */"
+ }
+ ],
+ [
+ "api_versions_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_module_version_t",
+ "api_versions",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x5f0d99d6",
+ "options": {}
+ }
+ ],
+ [
+ "trace_plugin_msg_ids",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "plugin_name",
+ 128
+ ],
+ [
+ "u16",
+ "first_msg_id"
+ ],
+ [
+ "u16",
+ "last_msg_id"
+ ],
+ {
+ "crc": "0xf476d3ce",
+ "options": {},
+ "comment": "/*\n * Trace the plugin message-id allocator\n * so we stand a chance of dealing with different sets of plugins\n * at api trace replay time\n */"
+ }
+ ],
+ [
+ "sockclnt_create",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ {
+ "crc": "0x455fb9c4",
+ "options": {},
+ "comment": "/*\n * Create a socket client registration.\n */"
+ }
+ ],
+ [
+ "sockclnt_create_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u16",
+ "count"
+ ],
+ [
+ "vl_api_message_table_entry_t",
+ "message_table",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x35166268",
+ "options": {}
+ }
+ ],
+ [
+ "sockclnt_delete",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ {
+ "crc": "0x8ac76db6",
+ "options": {},
+ "comment": "/*\n * Delete a client registration\n */"
+ }
+ ],
+ [
+ "sockclnt_delete_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ {
+ "crc": "0x8f38b1ee",
+ "options": {}
+ }
+ ],
+ [
+ "sock_init_shm",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "requested_size"
+ ],
+ [
+ "u8",
+ "nitems"
+ ],
+ [
+ "u64",
+ "configs",
+ 0,
+ "nitems"
+ ],
+ {
+ "crc": "0x51646d92",
+ "options": {},
+ "comment": "/*\n * Initialize shm api over socket api\n */"
+ }
+ ],
+ [
+ "sock_init_shm_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804",
+ "options": {}
+ }
+ ],
+ [
+ "memclnt_keepalive",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {},
+ "comment": "/*\n * Memory client ping / response\n * Only sent on inactive connections\n */"
+ }
+ ],
+ [
+ "memclnt_keepalive_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804",
+ "options": {}
+ }
+ ],
+ [
+ "control_ping",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {},
+ "comment": "/** \\brief Control ping from client to api server request\n @param client_index - opaque cookie to identify the sender\n @param context - sender context, to match reply w/ request\n*/"
+ }
+ ],
+ [
+ "control_ping_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "vpe_pid"
+ ],
+ {
+ "crc": "0xf6b0b8ca",
+ "options": {},
+ "comment": "/** \\brief Control ping from the client to the server response\n @param client_index - opaque cookie to identify the sender\n @param context - sender context, to match reply w/ request\n @param retval - return code for the request\n @param vpe_pid - the pid of the vpe, returned by the server\n*/"
+ }
+ ],
+ [
+ "memclnt_create_v2",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "ctx_quota"
+ ],
+ [
+ "u64",
+ "input_queue"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ [
+ "u32",
+ "api_versions",
+ 8
+ ],
+ [
+ "bool",
+ "keepalive",
+ {
+ "default": "true"
+ }
+ ],
+ {
+ "crc": "0xc4bd4882",
+ "options": {}
+ }
+ ],
+ [
+ "memclnt_create_v2_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u64",
+ "message_table"
+ ],
+ {
+ "crc": "0x42ec4560",
+ "options": {}
+ }
+ ],
+ [
+ "get_api_json",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {}
+ }
+ ],
+ [
+ "get_api_json_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "string",
+ "json",
+ 0
+ ],
+ {
+ "crc": "0xea715b59",
+ "options": {}
+ }
+ ]
+ ],
+ "unions": [],
+ "enums": [],
+ "enumflags": [],
+ "services": {
+ "memclnt_rx_thread_suspend": {
+ "reply": "null"
+ },
+ "memclnt_read_timeout": {
+ "reply": "null"
+ },
+ "rx_thread_exit": {
+ "reply": "null"
+ },
+ "trace_plugin_msg_ids": {
+ "reply": "null"
+ },
+ "memclnt_create": {
+ "reply": "memclnt_create_reply"
+ },
+ "memclnt_delete": {
+ "reply": "memclnt_delete_reply"
+ },
+ "rpc_call": {
+ "reply": "rpc_call_reply"
+ },
+ "get_first_msg_id": {
+ "reply": "get_first_msg_id_reply"
+ },
+ "api_versions": {
+ "reply": "api_versions_reply"
+ },
+ "sockclnt_create": {
+ "reply": "sockclnt_create_reply"
+ },
+ "sockclnt_delete": {
+ "reply": "sockclnt_delete_reply"
+ },
+ "sock_init_shm": {
+ "reply": "sock_init_shm_reply"
+ },
+ "memclnt_keepalive": {
+ "reply": "memclnt_keepalive_reply"
+ },
+ "control_ping": {
+ "reply": "control_ping_reply"
+ },
+ "memclnt_create_v2": {
+ "reply": "memclnt_create_v2_reply"
+ },
+ "get_api_json": {
+ "reply": "get_api_json_reply"
+ }
+ },
+ "options": {
+ "version": "2.1.0"
+ },
+ "aliases": {},
+ "vl_api_version": "0xb197c551",
+ "imports": [],
+ "counters": [],
+ "paths": []
+}
diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py
index 5a9e0a70c64..30c00cd8dd3 100644
--- a/src/vpp-api/python/vpp_papi/vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi/vpp_papi.py
@@ -18,7 +18,6 @@ from __future__ import print_function
from __future__ import absolute_import
import ctypes
import ipaddress
-import sys
import multiprocessing as mp
import os
import queue
@@ -30,6 +29,7 @@ import fnmatch
import weakref
import atexit
import time
+import pkg_resources
from .vpp_format import verify_enum_hint
from .vpp_serializer import VPPType, VPPEnumType, VPPEnumFlagType, VPPUnionType
from .vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias
@@ -281,15 +281,28 @@ class VPPApiJSONFiles:
@classmethod
def process_json_file(self, apidef_file):
- return self._process_json(apidef_file.read())
+ api = json.load(apidef_file)
+ return self._process_json(api)
@classmethod
def process_json_str(self, json_str):
- return self._process_json(json_str)
+ api = json.loads(json_str)
+ return self._process_json(api)
+
+ @classmethod
+ def process_json_array_str(self, json_str):
+ services = {}
+ messages = {}
+
+ apis = json.loads(json_str)
+ for a in apis:
+ m, s = self._process_json(a)
+ messages.update(m)
+ services.update(s)
+ return messages, services
@staticmethod
- def _process_json(json_str): # -> Tuple[Dict, Dict]
- api = json.loads(json_str)
+ def _process_json(api): # -> Tuple[Dict, Dict]
types = {}
services = {}
messages = {}
@@ -373,7 +386,6 @@ class VPPApiJSONFiles:
try:
messages[m[0]] = VPPMessage(m[0], m[1:])
except VPPNotImplementedError:
- ### OLE FIXME
logger.error("Not implemented error for {}".format(m[0]))
except KeyError:
pass
@@ -435,6 +447,7 @@ class VPPApiClient:
read_timeout=5,
use_socket=True,
server_address="/run/vpp/api.sock",
+ bootstrapapi=False,
):
"""Create a VPP API object.
@@ -472,25 +485,37 @@ class VPPApiClient:
self.server_address = server_address
self._apifiles = apifiles
self.stats = {}
+ self.bootstrapapi = bootstrapapi
- if self.apidir is None and hasattr(self.__class__, "apidir"):
- # Keep supporting the old style of providing apidir.
- self.apidir = self.__class__.apidir
- try:
- self.apifiles, self.messages, self.services = VPPApiJSONFiles.load_api(
- apifiles, self.apidir
+ if not bootstrapapi:
+ if self.apidir is None and hasattr(self.__class__, "apidir"):
+ # Keep supporting the old style of providing apidir.
+ self.apidir = self.__class__.apidir
+ try:
+ self.apifiles, self.messages, self.services = VPPApiJSONFiles.load_api(
+ apifiles, self.apidir
+ )
+ except VPPRuntimeError as e:
+ if testmode:
+ self.apifiles = []
+ else:
+ raise e
+ else:
+ # Bootstrap the API (memclnt.api bundled with VPP PAPI)
+ resource_path = "/".join(("data", "memclnt.api.json"))
+ file_content = pkg_resources.resource_string(__name__, resource_path)
+ self.messages, self.services = VPPApiJSONFiles.process_json_str(
+ file_content
)
- except VPPRuntimeError as e:
- if testmode:
- self.apifiles = []
- else:
- raise e
# Basic sanity check
if len(self.messages) == 0 and not testmode:
raise VPPValueError(1, "Missing JSON message definitions")
- if not (verify_enum_hint(VppEnum.vl_api_address_family_t)):
- raise VPPRuntimeError("Invalid address family hints. " "Cannot continue.")
+ if not bootstrapapi:
+ if not (verify_enum_hint(VppEnum.vl_api_address_family_t)):
+ raise VPPRuntimeError(
+ "Invalid address family hints. " "Cannot continue."
+ )
self.transport = VppTransport(
self, read_timeout=read_timeout, server_address=server_address
@@ -573,6 +598,21 @@ class VPPApiClient:
else:
self.logger.debug("No such message type or failed CRC checksum: %s", n)
+ def get_api_definitions(self):
+ """get_api_definition. Bootstrap from the embedded memclnt.api.json file."""
+
+ # Bootstrap so we can call the get_api_json function
+ self._register_functions(do_async=False)
+
+ r = self.api.get_api_json()
+ if r.retval != 0:
+ raise VPPApiError("Failed to load API definitions from VPP")
+
+ # Process JSON
+ m, s = VPPApiJSONFiles.process_json_array_str(r.json)
+ self.messages.update(m)
+ self.services.update(s)
+
def connect_internal(self, name, msg_handler, chroot_prefix, rx_qlen, do_async):
pfx = chroot_prefix.encode("utf-8") if chroot_prefix else None
@@ -580,6 +620,10 @@ class VPPApiClient:
if rv != 0:
raise VPPIOError(2, "Connect failed")
self.vpp_dictionary_maxid = self.transport.msg_table_max_index()
+
+ # Register functions
+ if self.bootstrapapi:
+ self.get_api_definitions()
self._register_functions(do_async=do_async)
# Initialise control ping
@@ -588,6 +632,7 @@ class VPPApiClient:
("control_ping" + "_" + crc[2:])
)
self.control_ping_msgdef = self.messages["control_ping"]
+
if self.async_thread:
self.event_thread = threading.Thread(target=self.thread_msg_handler)
self.event_thread.daemon = True
@@ -659,6 +704,7 @@ class VPPApiClient:
)
(i, ci, context), size = header.unpack(msg, 0)
+
if self.id_names[i] == "rx_thread_exit":
return