diff options
author | Ole Troan <otroan@employees.org> | 2024-01-23 18:56:23 +0100 |
---|---|---|
committer | Andrew Yourtchenko <ayourtch@gmail.com> | 2024-01-29 22:57:31 +0000 |
commit | ac0babd412e3b5282136a5c5c5be2c4cc4be6895 (patch) | |
tree | 5c58249463d7121a163534e55b1d5f6f27ae7967 | |
parent | f34b6800de78d99192dc5757a4845e2668749c40 (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.cmake | 2 | ||||
-rwxr-xr-x | src/tools/vppapigen/generate_json.py | 9 | ||||
-rwxr-xr-x | src/tools/vppapigen/vppapigen_c.py | 2 | ||||
-rw-r--r-- | src/tools/vppapigen/vppapigen_json.py | 30 | ||||
-rw-r--r-- | src/vlibapi/api_common.h | 2 | ||||
-rw-r--r-- | src/vlibmemory/memclnt.api | 19 | ||||
-rw-r--r-- | src/vlibmemory/memclnt_api.c | 36 | ||||
-rw-r--r-- | src/vpp-api/python/setup.py | 4 | ||||
-rw-r--r-- | src/vpp-api/python/vpp_papi/data/memclnt.api.json | 809 | ||||
-rw-r--r-- | src/vpp-api/python/vpp_papi/vpp_papi.py | 84 |
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 |