diff options
Diffstat (limited to 'src/vpp-api')
27 files changed, 2420 insertions, 389 deletions
diff --git a/src/vpp-api/client/client.c b/src/vpp-api/client/client.c index 7a30792402c..d59273ed6cb 100644 --- a/src/vpp-api/client/client.c +++ b/src/vpp-api/client/client.c @@ -305,6 +305,8 @@ vac_connect (char * name, char * chroot_prefix, vac_callback_t cb, } /* Start read timeout thread */ + timeout_in_progress = false; + timeout_thread_cancelled = false; rv = pthread_create(&pm->timeout_thread_handle, NULL, vac_timeout_thread_fn, 0); if (rv) { diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index 2e8e1fadf8d..359813f8d57 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -225,7 +225,7 @@ stat_vec_combined_init (vlib_counter_t c) */ static stat_segment_data_t copy_data (vlib_stats_entry_t *ep, u32 index2, char *name, - stat_client_main_t *sm) + stat_client_main_t *sm, bool via_symlink) { stat_segment_data_t result = { 0 }; int i; @@ -235,6 +235,7 @@ copy_data (vlib_stats_entry_t *ep, u32 index2, char *name, assert (sm->shared_header); result.type = ep->type; + result.via_symlink = via_symlink; result.name = strdup (name ? name : ep->name); switch (ep->type) @@ -287,7 +288,9 @@ copy_data (vlib_stats_entry_t *ep, u32 index2, char *name, { vlib_stats_entry_t *ep2; ep2 = vec_elt_at_index (sm->directory_vector, ep->index1); - return copy_data (ep2, ep->index2, ep->name, sm); + /* We do not intend to return the "result", avoid a leak */ + free (result.name); + return copy_data (ep2, ep->index2, ep->name, sm, true); } case STAT_DIR_TYPE_EMPTY: @@ -409,11 +412,20 @@ stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm) if (stat_segment_access_start (&sa, sm)) return 0; + /* preallocate the elements. + * This takes care of a special case where + * the vec_len(stats) == 0, + * such that we return a vector of + * length 0, rather than a null pointer + * (since null pointer is an error) + */ + vec_alloc (res, vec_len (stats)); + for (i = 0; i < vec_len (stats); i++) { /* Collect counter */ ep = vec_elt_at_index (sm->directory_vector, stats[i]); - vec_add1 (res, copy_data (ep, ~0, 0, sm)); + vec_add1 (res, copy_data (ep, ~0, 0, sm, false)); } if (stat_segment_access_end (&sa, sm)) @@ -421,6 +433,8 @@ stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm) fprintf (stderr, "Epoch changed while reading, invalid results\n"); // TODO increase counter + if (res) + stat_segment_data_free (res); return 0; } @@ -472,7 +486,7 @@ stat_segment_dump_entry_r (uint32_t index, stat_client_main_t * sm) /* Collect counter */ ep = vec_elt_at_index (sm->directory_vector, index); - vec_add1 (res, copy_data (ep, ~0, 0, sm)); + vec_add1 (res, copy_data (ep, ~0, 0, sm, false)); if (stat_segment_access_end (&sa, sm)) return res; @@ -500,6 +514,11 @@ stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm) return 0; vec = get_stat_vector_r (sm); ep = vec_elt_at_index (vec, index); + if (ep->type == STAT_DIR_TYPE_EMPTY) + { + stat_segment_access_end (&sa, sm); + return 0; + } if (!stat_segment_access_end (&sa, sm)) return 0; return strdup (ep->name); diff --git a/src/vpp-api/client/stat_client.h b/src/vpp-api/client/stat_client.h index 26752718d90..d9671c69ff2 100644 --- a/src/vpp-api/client/stat_client.h +++ b/src/vpp-api/client/stat_client.h @@ -36,6 +36,7 @@ typedef struct { char *name; stat_directory_type_t type; + bool via_symlink; union { double scalar_value; diff --git a/src/vpp-api/client/test.c b/src/vpp-api/client/test.c index 86d6aef79be..c242e6611a4 100644 --- a/src/vpp-api/client/test.c +++ b/src/vpp-api/client/test.c @@ -70,7 +70,6 @@ wrap_vac_callback (unsigned char *data, int len) static void test_connect () { - static int i; int rv = vac_connect("vac_client", NULL, wrap_vac_callback, 32 /* rx queue-length*/); if (rv != 0) { printf("Connect failed: %d\n", rv); @@ -78,7 +77,6 @@ test_connect () } printf("."); vac_disconnect(); - i++; } static void diff --git a/src/vpp-api/python/CMakeLists.txt b/src/vpp-api/python/CMakeLists.txt index 789a72233e6..3059619ff21 100644 --- a/src/vpp-api/python/CMakeLists.txt +++ b/src/vpp-api/python/CMakeLists.txt @@ -11,27 +11,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -if (CMAKE_VERSION VERSION_LESS 3.12) - find_package(PythonInterp 2.7) -else() - find_package(Python3 COMPONENTS Interpreter) - set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND}) - set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) -endif() +find_package(Python3 REQUIRED COMPONENTS Interpreter) +set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND}) +set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) -if(PYTHONINTERP_FOUND) - install( - CODE " - execute_process( - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} ./setup.py - install - --root=\$ENV{DESTDIR}/ - --prefix=${CMAKE_INSTALL_PREFIX} - --single-version-externally-managed - bdist_egg - OUTPUT_QUIET - )" - COMPONENT vpp-api-python - ) -endif() +install( + CODE " + execute_process( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${PYTHON_EXECUTABLE} ./setup.py + install + --root=\$ENV{DESTDIR}/ + --prefix=${CMAKE_INSTALL_PREFIX} + --single-version-externally-managed + bdist_egg + OUTPUT_QUIET + )" + COMPONENT vpp-api-python +) 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/__init__.py b/src/vpp-api/python/vpp_papi/__init__.py index f87b6480d4e..dc58c1e18cb 100644 --- a/src/vpp-api/python/vpp_papi/__init__.py +++ b/src/vpp-api/python/vpp_papi/__init__.py @@ -14,5 +14,5 @@ import pkg_resources # part of setuptools try: __version__ = pkg_resources.get_distribution("vpp_papi").version -except (pkg_resources.DistributionNotFound): +except pkg_resources.DistributionNotFound: """Can't find vpp_papi via setuptools""" 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/macaddress.py b/src/vpp-api/python/vpp_papi/macaddress.py index 8799bd7be24..66349a3c19a 100644 --- a/src/vpp-api/python/vpp_papi/macaddress.py +++ b/src/vpp-api/python/vpp_papi/macaddress.py @@ -53,7 +53,6 @@ class MACAddress: return "%s(%s)" % (self.__class__.__name__, self.mac_string) def __eq__(self, other): - if not isinstance(other, MACAddress): try: # if it looks like a mac address, we'll take it. diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py index 2b21c83966a..51c024aa3ab 100644 --- a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py +++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py @@ -24,8 +24,7 @@ from vpp_papi import vpp_transport_shmem class TestVppPapiVPPApiClient(unittest.TestCase): def test_getcontext(self): - vpp_papi.VPPApiClient.apidir = "." - c = vpp_papi.VPPApiClient(testmode=True, use_socket=True) + c = vpp_papi.VPPApiClient(apidir=".", testmode=True, use_socket=True) # reset initialization at module load time. c.get_context.context = mp.Value(ctypes.c_uint, 0) @@ -39,8 +38,7 @@ class TestVppPapiVPPApiClientMp(unittest.TestCase): # run_tests.py (eg. make test TEST_JOBS=10) def test_get_context_mp(self): - vpp_papi.VPPApiClient.apidir = "." - c = vpp_papi.VPPApiClient(testmode=True, use_socket=True) + c = vpp_papi.VPPApiClient(apidir=".", testmode=True, use_socket=True) # reset initialization at module load time. c.get_context.context = mp.Value(ctypes.c_uint, 0) diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py index eee38f00632..f0d2846214a 100755 --- a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py @@ -426,6 +426,32 @@ class TestAddType(unittest.TestCase): nt, size = s.unpack(b) self.assertEqual(len(b), size) + # Try same with VLA u8 + byte_array = [b"\0"] * (10) + vla_u8 = VPPType("vla_u8", [["u8", "length"], ["u8", "data", 0, "length"]]) + b = vla_u8.pack({"length": len(byte_array), "data": byte_array}) + nt, size = vla_u8.unpack(b) + + # VLA Array of fixed length strings + fixed_string = VPPType("fixed_string", [["string", "data", 32]]) + s = VPPType( + "string_vla", [["u32", "length"], ["fixed_string", "services", 0, "length"]] + ) + + string_list = [{"data": "foobar1"}, {"data": "foobar2"}] + b = s.pack({"length": 2, "services": string_list}) + nt, size = s.unpack(b) + + # Try same with u8 + fixed_u8 = VPPType("fixed_u8", [["u8", "data", 32]]) + s = VPPType( + "u8_vla", [["u32", "length"], ["fixed_string", "services", 0, "length"]] + ) + + u8_list = [{"data": "foobar1"}, {"data": "foobar2"}] + b = s.pack({"length": 2, "services": u8_list}) + nt, size = s.unpack(b) + def test_message(self): foo = VPPMessage( "foo", @@ -442,7 +468,6 @@ class TestAddType(unittest.TestCase): self.assertEqual(nt.something, 200) def test_abf(self): - fib_mpls_label = VPPType( "vl_api_fib_mpls_label_t", [["u8", "is_uniform"], ["u32", "label"], ["u8", "ttl"], ["u8", "exp"]], @@ -535,7 +560,6 @@ class TestAddType(unittest.TestCase): ) def test_bier(self): - bier_table_id = VPPType( "vl_api_bier_table_id_t", [["u8", "bt_set"], ["u8", "bt_sub_domain"], ["u8", "bt_hdr_len_id"]], diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py index 1e5d23e59b7..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 @@ -154,7 +154,7 @@ class VPPValueError(ValueError): class VPPApiJSONFiles: @classmethod - def find_api_dir(cls, dirs): + def find_api_dir(cls, dirs=[]): """Attempt to find the best directory in which API definition files may reside. If the value VPP_API_DIR exists in the environment then it is first on the search list. If we're inside a recognized @@ -170,6 +170,9 @@ class VPPApiJSONFiles: # in which case, plot a course to likely places in the src tree import __main__ as main + if os.getenv("VPP_API_DIR"): + dirs.append(os.getenv("VPP_API_DIR")) + if hasattr(main, "__file__"): # get the path of the calling script localdir = os.path.dirname(os.path.realpath(main.__file__)) @@ -286,6 +289,18 @@ class VPPApiJSONFiles: 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(api): # -> Tuple[Dict, Dict] types = {} @@ -371,12 +386,35 @@ 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 return messages, services + @staticmethod + def load_api(apifiles=None, apidir=None): + messages = {} + services = {} + if not apifiles: + # Pick up API definitions from default directory + try: + if isinstance(apidir, list): + apifiles = [] + for d in apidir: + apifiles += VPPApiJSONFiles.find_api_files(d) + else: + apifiles = VPPApiJSONFiles.find_api_files(apidir) + except (RuntimeError, VPPApiError): + raise VPPRuntimeError + + for file in apifiles: + with open(file) as apidef_file: + m, s = VPPApiJSONFiles.process_json_file(apidef_file) + messages.update(m) + services.update(s) + + return apifiles, messages, services + class VPPApiClient: """VPP interface. @@ -391,7 +429,6 @@ class VPPApiClient: these messages in a background thread. """ - apidir = None VPPApiError = VPPApiError VPPRuntimeError = VPPRuntimeError VPPValueError = VPPValueError @@ -402,6 +439,7 @@ class VPPApiClient: self, *, apifiles=None, + apidir=None, testmode=False, async_thread=True, logger=None, @@ -409,6 +447,7 @@ class VPPApiClient: read_timeout=5, use_socket=True, server_address="/run/vpp/api.sock", + bootstrapapi=False, ): """Create a VPP API object. @@ -436,6 +475,7 @@ class VPPApiClient: self.id_msgdef = [] self.header = VPPType("header", [["u16", "msgid"], ["u32", "client_index"]]) self.apifiles = [] + self.apidir = apidir self.event_callback = None self.message_queue = queue.Queue() self.read_timeout = read_timeout @@ -445,31 +485,37 @@ class VPPApiClient: self.server_address = server_address self._apifiles = apifiles self.stats = {} + self.bootstrapapi = bootstrapapi - if not apifiles: - # Pick up API definitions from default directory + 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: - apifiles = VPPApiJSONFiles.find_api_files(self.apidir) - except (RuntimeError, VPPApiError): - # In test mode we don't care that we can't find the API files + self.apifiles, self.messages, self.services = VPPApiJSONFiles.load_api( + apifiles, self.apidir + ) + except VPPRuntimeError as e: if testmode: - apifiles = [] + self.apifiles = [] else: - raise VPPRuntimeError - - for file in apifiles: - with open(file) as apidef_file: - m, s = VPPApiJSONFiles.process_json_file(apidef_file) - self.messages.update(m) - self.services.update(s) - - self.apifiles = apifiles + 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 + ) # 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 @@ -525,6 +571,13 @@ class VPPApiClient: return f + def make_pack_function(self, msg, i, multipart): + def f(**kwargs): + return self._call_vpp_pack(i, msg, **kwargs) + + f.msg = msg + return f + def _register_functions(self, do_async=False): self.id_names = [None] * (self.vpp_dictionary_maxid + 1) self.id_msgdef = [None] * (self.vpp_dictionary_maxid + 1) @@ -539,17 +592,38 @@ class VPPApiClient: # Create function for client side messages. if name in self.services: f = self.make_function(msg, i, self.services[name], do_async) + f_pack = self.make_pack_function(msg, i, self.services[name]) setattr(self._api, name, FuncWrapper(f)) + setattr(self._api, name + "_pack", FuncWrapper(f_pack)) 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 - rv = self.transport.connect(name, pfx, msg_handler, rx_qlen) + rv = self.transport.connect(name, pfx, msg_handler, rx_qlen, do_async) 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 @@ -558,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 @@ -629,6 +704,7 @@ class VPPApiClient: ) (i, ci, context), size = header.unpack(msg, 0) + if self.id_names[i] == "rx_thread_exit": return @@ -831,6 +907,13 @@ class VPPApiClient: self.transport.write(b) return context + def _call_vpp_pack(self, i, msg, **kwargs): + """Given a message, return the binary representation.""" + kwargs["_vl_msg_id"] = i + kwargs["client_index"] = 0 + kwargs["context"] = 0 + return msg.pack(kwargs) + def read_blocking(self, no_type_conversion=False, timeout=None): """Get next received message from transport within timeout, decoded. diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py index d7da457ea1a..d724cb33ce9 100644 --- a/src/vpp-api/python/vpp_papi/vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py @@ -135,7 +135,7 @@ class String(Packer): def __init__(self, name, num, options): self.name = name self.num = num - self.size = 1 + self.size = num if num else 1 self.length_field_packer = BaseTypes("u32") self.limit = options["limit"] if "limit" in options else num self.fixed = True if num else False @@ -304,9 +304,8 @@ class VLAList(Packer): len(lst), kwargs[self.length_field] ) ) - # u8 array - if self.packer.size == 1: + if self.packer.size == 1 and self.field_type == "u8": if isinstance(lst, list): return b"".join(lst) return bytes(lst) @@ -321,7 +320,7 @@ class VLAList(Packer): total = 0 # u8 array - if self.packer.size == 1: + if self.packer.size == 1 and self.field_type == "u8": if result[self.index] == 0: return b"", 0 p = BaseTypes("u8", result[self.index]) @@ -618,7 +617,6 @@ class VPPType(Packer): self.packers.append(p) size += p.size - self.size = size self.tuple = collections.namedtuple(name, self.fields, rename=True) types[name] = self diff --git a/src/vpp-api/python/vpp_papi/vpp_stats.py b/src/vpp-api/python/vpp_papi/vpp_stats.py index 4a342b68a8f..aa9ff85b3c7 100755 --- a/src/vpp-api/python/vpp_papi/vpp_stats.py +++ b/src/vpp-api/python/vpp_papi/vpp_stats.py @@ -54,7 +54,7 @@ import re def recv_fd(sock): """Get file descriptor for memory map""" fds = array.array("i") # Array of ints - _, ancdata, _, _ = sock.recvmsg(0, socket.CMSG_LEN(4)) + _, ancdata, _, _ = sock.recvmsg(0, socket.CMSG_SPACE(4)) for cmsg_level, cmsg_type, cmsg_data in ancdata: if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) @@ -136,6 +136,12 @@ class VPPStats: if self.connected: return sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + + # Our connect races the corresponding recv_fds call in VPP, if we beat + # VPP then we will try (unsuccessfully) to receive file descriptors and + # will have gone away before VPP can respond to our connect. A short + # timeout here stops this error occurring. + sock.settimeout(1) sock.connect(self.socketname) mfd = recv_fd(sock) diff --git a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py index 3a8c332a00a..174ab74d0b8 100644 --- a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py +++ b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py @@ -42,7 +42,7 @@ class VppTransport: while True: try: rlist, _, _ = select.select([self.socket, self.sque._reader], [], []) - except socket.error: + except (socket.error, ValueError): # Terminate thread logging.error("select failed") self.q.put(None) @@ -65,14 +65,14 @@ class VppTransport: return # Put either to local queue or if context == 0 # callback queue - if self.parent.has_context(msg): + if not self.do_async and self.parent.has_context(msg): self.q.put(msg) else: self.parent.msg_handler_async(msg) else: raise VppTransportSocketIOError(2, "Unknown response from select") - def connect(self, name, pfx, msg_handler, rx_qlen): + def connect(self, name, pfx, msg_handler, rx_qlen, do_async=False): # TODO: Reorder the actions and add "roll-backs", # to restore clean disconnect state when failure happens durng connect. @@ -125,6 +125,7 @@ class VppTransport: self.message_table[n] = m.index self.message_thread.daemon = True + self.do_async = do_async self.message_thread.start() return 0 diff --git a/src/vpp-api/vapi/CMakeLists.txt b/src/vpp-api/vapi/CMakeLists.txt index e01692210bb..e53d3e8b238 100644 --- a/src/vpp-api/vapi/CMakeLists.txt +++ b/src/vpp-api/vapi/CMakeLists.txt @@ -45,7 +45,7 @@ install( vapi_json_parser.py vapi_cpp_gen.py DESTINATION - share/vpp + ${CMAKE_INSTALL_DATADIR}/vpp COMPONENT vpp-dev ) diff --git a/src/vpp-api/vapi/fake.api.json b/src/vpp-api/vapi/fake.api.json index 24c9f4dbfa1..f7238c468fa 100644 --- a/src/vpp-api/vapi/fake.api.json +++ b/src/vpp-api/vapi/fake.api.json @@ -10,6 +10,8 @@ }, "enums" : [ ], + "enumflags" : [ + ], "unions" : [ ], "types" : [ diff --git a/src/vpp-api/vapi/vapi.c b/src/vpp-api/vapi/vapi.c index 7808bec8521..022f023aeb0 100644 --- a/src/vpp-api/vapi/vapi.c +++ b/src/vpp-api/vapi/vapi.c @@ -63,7 +63,8 @@ typedef struct u32 context; vapi_cb_t callback; void *callback_ctx; - bool is_dump; + vapi_msg_id_t response_id; + enum vapi_request_type type; } vapi_req_t; static const u32 context_counter_mask = (1 << 31); @@ -97,8 +98,11 @@ struct vapi_ctx_s bool connected; bool handle_keepalives; pthread_mutex_t requests_mutex; + bool use_uds; svm_queue_t *vl_input_queue; + clib_socket_t client_socket; + clib_time_t time; u32 my_client_index; /** client message index hash table */ uword *msg_index_by_name_and_crc; @@ -137,15 +141,17 @@ vapi_requests_end (vapi_ctx_t ctx) } void -vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump, - vapi_cb_t callback, void *callback_ctx) +vapi_store_request (vapi_ctx_t ctx, u32 context, vapi_msg_id_t response_id, + enum vapi_request_type request_type, vapi_cb_t callback, + void *callback_ctx) { assert (!vapi_requests_full (ctx)); /* if the mutex is not held, bad things will happen */ assert (0 != pthread_mutex_trylock (&ctx->requests_mutex)); const int requests_end = vapi_requests_end (ctx); vapi_req_t *slot = &ctx->requests[requests_end]; - slot->is_dump = is_dump; + slot->type = request_type; + slot->response_id = response_id; slot->context = context; slot->callback = callback; slot->callback_ctx = callback_ctx; @@ -227,8 +233,8 @@ vapi_to_be_freed_validate () #endif -void * -vapi_msg_alloc (vapi_ctx_t ctx, size_t size) +static void * +vapi_shm_msg_alloc (vapi_ctx_t ctx, size_t size) { if (!ctx->connected) { @@ -242,6 +248,23 @@ vapi_msg_alloc (vapi_ctx_t ctx, size_t size) return rv; } +static void * +vapi_sock_msg_alloc (size_t size) +{ + u8 *rv = 0; + vec_validate_init_empty (rv, size - 1, 0); + return rv; +} + +void * +vapi_msg_alloc (vapi_ctx_t ctx, size_t size) +{ + if (ctx->use_uds) + return vapi_sock_msg_alloc (size); + + return vapi_shm_msg_alloc (ctx, size); +} + void vapi_msg_free (vapi_ctx_t ctx, void *msg) { @@ -249,10 +272,19 @@ vapi_msg_free (vapi_ctx_t ctx, void *msg) { return; } + #if VAPI_DEBUG_ALLOC vapi_trace_free (msg); #endif - vl_msg_api_free (msg); + + if (ctx->use_uds) + { + vec_free (msg); + } + else + { + vl_msg_api_free (msg); + } } vapi_msg_id_t @@ -291,6 +323,7 @@ vapi_ctx_alloc (vapi_ctx_t * result) } pthread_mutex_init (&ctx->requests_mutex, NULL); *result = ctx; + clib_time_init (&ctx->time); return VAPI_OK; fail: vapi_ctx_free (ctx); @@ -340,6 +373,205 @@ vapi_api_name_and_crc_free (vapi_ctx_t ctx) hash_free (ctx->msg_index_by_name_and_crc); } +static vapi_error_e +vapi_sock_get_errno (int err) +{ + switch (err) + { + case ENOTSOCK: + return VAPI_ENOTSOCK; + case EACCES: + return VAPI_EACCES; + case ECONNRESET: + return VAPI_ECONNRESET; + default: + break; + } + return VAPI_ESOCK_FAILURE; +} + +static vapi_error_e +vapi_sock_send (vapi_ctx_t ctx, u8 *msg) +{ + size_t n; + struct msghdr hdr; + + const size_t len = vec_len (msg); + const size_t total_len = len + sizeof (msgbuf_t); + + msgbuf_t msgbuf1 = { + .q = 0, + .gc_mark_timestamp = 0, + .data_len = htonl (len), + }; + + struct iovec bufs[2] = { + [0] = { .iov_base = &msgbuf1, .iov_len = sizeof (msgbuf1) }, + [1] = { .iov_base = msg, .iov_len = len }, + }; + + clib_memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = bufs; + hdr.msg_iovlen = 2; + + n = sendmsg (ctx->client_socket.fd, &hdr, 0); + if (n < 0) + { + return vapi_sock_get_errno (errno); + } + + if (n < total_len) + { + return VAPI_EAGAIN; + } + + vec_free (msg); + + return VAPI_OK; +} + +static vapi_error_e +vapi_sock_send2 (vapi_ctx_t ctx, u8 *msg1, u8 *msg2) +{ + size_t n; + struct msghdr hdr; + + const size_t len1 = vec_len (msg1); + const size_t len2 = vec_len (msg2); + const size_t total_len = len1 + len2 + 2 * sizeof (msgbuf_t); + + msgbuf_t msgbuf1 = { + .q = 0, + .gc_mark_timestamp = 0, + .data_len = htonl (len1), + }; + + msgbuf_t msgbuf2 = { + .q = 0, + .gc_mark_timestamp = 0, + .data_len = htonl (len2), + }; + + struct iovec bufs[4] = { + [0] = { .iov_base = &msgbuf1, .iov_len = sizeof (msgbuf1) }, + [1] = { .iov_base = msg1, .iov_len = len1 }, + [2] = { .iov_base = &msgbuf2, .iov_len = sizeof (msgbuf2) }, + [3] = { .iov_base = msg2, .iov_len = len2 }, + }; + + clib_memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = bufs; + hdr.msg_iovlen = 4; + + n = sendmsg (ctx->client_socket.fd, &hdr, 0); + if (n < 0) + { + return vapi_sock_get_errno (errno); + } + + if (n < total_len) + { + return VAPI_EAGAIN; + } + + vec_free (msg1); + vec_free (msg2); + + return VAPI_OK; +} + +static vapi_error_e +vapi_sock_recv_internal (vapi_ctx_t ctx, u8 **vec_msg, u32 timeout) +{ + clib_socket_t *sock = &ctx->client_socket; + u32 data_len = 0, msg_size; + msgbuf_t *mbp = 0; + ssize_t n, current_rx_index; + f64 deadline; + vapi_error_e rv = VAPI_EAGAIN; + + if (ctx->client_socket.fd == 0) + return VAPI_ENOTSOCK; + + deadline = clib_time_now (&ctx->time) + timeout; + + while (1) + { + current_rx_index = vec_len (sock->rx_buffer); + while (current_rx_index < sizeof (*mbp)) + { + vec_validate (sock->rx_buffer, sizeof (*mbp) - 1); + n = recv (sock->fd, sock->rx_buffer + current_rx_index, + sizeof (*mbp) - current_rx_index, MSG_DONTWAIT); + if (n < 0) + { + if (errno == EAGAIN && clib_time_now (&ctx->time) >= deadline) + return VAPI_EAGAIN; + + if (errno == EAGAIN) + continue; + + clib_unix_warning ("socket_read"); + vec_set_len (sock->rx_buffer, current_rx_index); + return vapi_sock_get_errno (errno); + } + current_rx_index += n; + } + vec_set_len (sock->rx_buffer, current_rx_index); + + mbp = (msgbuf_t *) (sock->rx_buffer); + data_len = ntohl (mbp->data_len); + current_rx_index = vec_len (sock->rx_buffer); + vec_validate (sock->rx_buffer, current_rx_index + data_len); + mbp = (msgbuf_t *) (sock->rx_buffer); + msg_size = data_len + sizeof (*mbp); + + while (current_rx_index < msg_size) + { + n = recv (sock->fd, sock->rx_buffer + current_rx_index, + msg_size - current_rx_index, MSG_DONTWAIT); + if (n < 0) + { + if (errno == EAGAIN && clib_time_now (&ctx->time) >= deadline) + return VAPI_EAGAIN; + + if (errno == EAGAIN) + continue; + + clib_unix_warning ("socket_read"); + vec_set_len (sock->rx_buffer, current_rx_index); + return vapi_sock_get_errno (errno); + } + current_rx_index += n; + } + vec_set_len (sock->rx_buffer, current_rx_index); + + if (vec_len (sock->rx_buffer) >= data_len + sizeof (*mbp)) + { + if (data_len) + { + vec_add (*vec_msg, mbp->data, data_len); + rv = VAPI_OK; + } + else + { + *vec_msg = 0; + } + + if (vec_len (sock->rx_buffer) == data_len + sizeof (*mbp)) + vec_set_len (sock->rx_buffer, 0); + else + vec_delete (sock->rx_buffer, data_len + sizeof (*mbp), 0); + mbp = 0; + + /* Quit if we're out of data, and not expecting a ping reply */ + if (vec_len (sock->rx_buffer) == 0) + break; + } + } + return rv; +} + static void vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx, vl_api_memclnt_create_v2_reply_t *mp) @@ -374,6 +606,28 @@ vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx, } static void +vapi_sockclnt_create_reply_t_handler (vapi_ctx_t ctx, + vl_api_sockclnt_create_reply_t *mp) +{ + int i; + u8 *name_and_crc; + + ctx->my_client_index = mp->index; + + /* Clean out any previous hash table (unlikely) */ + vapi_api_name_and_crc_free (ctx); + + ctx->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword)); + + for (i = 0; i < be16toh (mp->count); i++) + { + name_and_crc = format (0, "%s%c", mp->message_table[i].name, 0); + hash_set_mem (ctx->msg_index_by_name_and_crc, name_and_crc, + be16toh (mp->message_table[i].index)); + } +} + +static void vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx, vl_api_memclnt_delete_reply_t *mp) { @@ -386,9 +640,17 @@ vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx, ctx->vl_input_queue = 0; } -int -vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota, - int input_queue_size, bool keepalive) +static void +vapi_sockclnt_delete_reply_t_handler (vapi_ctx_t ctx, + vl_api_sockclnt_delete_reply_t *mp) +{ + ctx->my_client_index = ~0; + ctx->vl_input_queue = 0; +} + +static int +vapi_shm_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota, + int input_queue_size, bool keepalive) { vl_api_memclnt_create_v2_t *mp; vl_api_memclnt_create_v2_reply_t *rp; @@ -403,7 +665,7 @@ vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota, if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0) { clib_warning ("shmem_hdr / input queue NULL"); - return -1; + return VAPI_ECON_FAIL; } clib_mem_unpoison (shmem_hdr, sizeof (*shmem_hdr)); @@ -446,7 +708,7 @@ vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota, ts = tsrem; } /* Timeout... */ - return -1; + return VAPI_ECON_FAIL; read_one_msg: VL_MSG_API_UNPOISON (rp); @@ -462,6 +724,223 @@ vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota, return (rv); } +static int +vapi_sock_client_connect (vapi_ctx_t ctx, char *path, const char *name) +{ + clib_error_t *error; + clib_socket_t *sock; + vl_api_sockclnt_create_t *mp; + vl_api_sockclnt_create_reply_t *rp; + int rv = 0; + u8 *msg = 0; + + ctx->my_client_index = ~0; + + if (ctx->client_socket.fd) + return VAPI_EINVAL; + + if (name == 0) + return VAPI_EINVAL; + + sock = &ctx->client_socket; + sock->config = path ? path : API_SOCKET_FILE; + sock->flags = CLIB_SOCKET_F_IS_CLIENT; + + if ((error = clib_socket_init (sock))) + { + clib_error_report (error); + return VAPI_ECON_FAIL; + } + + mp = vapi_sock_msg_alloc (sizeof (vl_api_sockclnt_create_t)); + mp->_vl_msg_id = ntohs (VL_API_SOCKCLNT_CREATE); + strncpy ((char *) mp->name, name, sizeof (mp->name) - 1); + + if (vapi_sock_send (ctx, (void *) mp) != VAPI_OK) + { + return VAPI_ECON_FAIL; + } + + while (1) + { + int qstatus; + struct timespec ts, tsrem; + int i; + + /* Wait up to 10 seconds */ + for (i = 0; i < 1000; i++) + { + qstatus = vapi_sock_recv_internal (ctx, &msg, 0); + + if (qstatus == 0) + goto read_one_msg; + ts.tv_sec = 0; + ts.tv_nsec = 10000 * 1000; /* 10 ms */ + while (nanosleep (&ts, &tsrem) < 0) + ts = tsrem; + } + /* Timeout... */ + return -1; + + read_one_msg: + if (vec_len (msg) == 0) + continue; + + rp = (void *) msg; + if (ntohs (rp->_vl_msg_id) != VL_API_SOCKCLNT_CREATE_REPLY) + { + clib_warning ("unexpected reply: id %d", ntohs (rp->_vl_msg_id)); + continue; + } + rv = clib_net_to_host_u32 (rp->response); + vapi_sockclnt_create_reply_t_handler (ctx, rp); + break; + } + return (rv); +} + +static void +vapi_shm_client_send_disconnect (vapi_ctx_t ctx, u8 do_cleanup) +{ + vl_api_memclnt_delete_t *mp; + vl_shmem_hdr_t *shmem_hdr; + api_main_t *am = vlibapi_get_main (); + + ASSERT (am->vlib_rp); + shmem_hdr = am->shmem_hdr; + ASSERT (shmem_hdr && shmem_hdr->vl_input_queue); + + mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_delete_t)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE); + mp->index = ctx->my_client_index; + mp->do_cleanup = do_cleanup; + + vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp); +} + +static vapi_error_e +vapi_sock_client_send_disconnect (vapi_ctx_t ctx) +{ + vl_api_sockclnt_delete_t *mp; + + mp = vapi_msg_alloc (ctx, sizeof (vl_api_sockclnt_delete_t)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_SOCKCLNT_DELETE); + mp->client_index = ctx->my_client_index; + + return vapi_sock_send (ctx, (void *) mp); +} + +static int +vapi_shm_client_disconnect (vapi_ctx_t ctx) +{ + vl_api_memclnt_delete_reply_t *rp; + svm_queue_t *vl_input_queue; + time_t begin; + msgbuf_t *msgbuf; + + vl_input_queue = ctx->vl_input_queue; + vapi_shm_client_send_disconnect (ctx, 0 /* wait for reply */); + + /* + * Have to be careful here, in case the client is disconnecting + * because e.g. the vlib process died, or is unresponsive. + */ + begin = time (0); + while (1) + { + time_t now; + + now = time (0); + + if (now >= (begin + 2)) + { + clib_warning ("peer unresponsive, give up"); + ctx->my_client_index = ~0; + return VAPI_ENORESP; + } + if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0) + continue; + + VL_MSG_API_UNPOISON (rp); + + /* drain the queue */ + if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY) + { + clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id)); + msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data)); + vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len)); + continue; + } + msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data)); + vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len)); + break; + } + + vapi_api_name_and_crc_free (ctx); + return 0; +} + +static vapi_error_e +vapi_sock_client_disconnect (vapi_ctx_t ctx) +{ + vl_api_sockclnt_delete_reply_t *rp; + u8 *msg = 0; + msgbuf_t *msgbuf; + int rv; + f64 deadline; + + deadline = clib_time_now (&ctx->time) + 2; + + do + { + rv = vapi_sock_client_send_disconnect (ctx); + } + while (clib_time_now (&ctx->time) < deadline && rv != VAPI_OK); + + while (1) + { + if (clib_time_now (&ctx->time) >= deadline) + { + clib_warning ("peer unresponsive, give up"); + ctx->my_client_index = ~0; + return VAPI_ENORESP; + } + + if (vapi_sock_recv_internal (ctx, &msg, 0) != VAPI_OK) + continue; + + msgbuf = (void *) msg; + rp = (void *) msgbuf->data; + /* drain the queue */ + if (ntohs (rp->_vl_msg_id) != VL_API_SOCKCLNT_DELETE_REPLY) + { + clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id)); + msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data)); + vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len)); + continue; + } + msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data)); + vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len)); + break; + } + + clib_socket_close (&ctx->client_socket); + vapi_api_name_and_crc_free (ctx); + return VAPI_OK; +} + +int +vapi_client_disconnect (vapi_ctx_t ctx) +{ + if (ctx->use_uds) + { + return vapi_sock_client_disconnect (ctx); + } + return vapi_shm_client_disconnect (ctx); +} + u32 vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc) { @@ -477,9 +956,9 @@ vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc) } vapi_error_e -vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix, - int max_outstanding_requests, int response_queue_size, - vapi_mode_e mode, bool handle_keepalives) +vapi_connect_ex (vapi_ctx_t ctx, const char *name, const char *path, + int max_outstanding_requests, int response_queue_size, + vapi_mode_e mode, bool handle_keepalives, bool use_uds) { int rv; @@ -487,7 +966,8 @@ vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix, { return VAPI_EINVAL; } - if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024 * 1024 * 32)) + + if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024L * 1024 * 32)) { return VAPI_ENOMEM; } @@ -503,28 +983,39 @@ vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix, clib_memset (ctx->requests, 0, size); /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */ ctx->requests_start = ctx->requests_count = 0; + ctx->use_uds = use_uds; - if (chroot_prefix) + if (use_uds) { - VAPI_DBG ("set memory root path `%s'", chroot_prefix); - vl_set_memory_root_path ((char *) chroot_prefix); - } - static char api_map[] = "/vpe-api"; - VAPI_DBG ("client api map `%s'", api_map); - if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0) - { - return VAPI_EMAP_FAIL; + if (vapi_sock_client_connect (ctx, (char *) path, name) < 0) + { + return VAPI_ECON_FAIL; + } } - VAPI_DBG ("connect client `%s'", name); - if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size, true) < - 0) + else { - vl_client_api_unmap (); - return VAPI_ECON_FAIL; - } + if (path) + { + VAPI_DBG ("set memory root path `%s'", path); + vl_set_memory_root_path ((char *) path); + } + static char api_map[] = "/vpe-api"; + VAPI_DBG ("client api map `%s'", api_map); + if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0) + { + return VAPI_EMAP_FAIL; + } + VAPI_DBG ("connect client `%s'", name); + if (vapi_shm_client_connect (ctx, (char *) name, 0, response_queue_size, + true) < 0) + { + vl_client_api_unmap (); + return VAPI_ECON_FAIL; + } #if VAPI_DEBUG_CONNECT VAPI_DBG ("start probing messages"); #endif + } int i; for (i = 0; i < __vapi_metadata.count; ++i) @@ -592,11 +1083,20 @@ vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix, } return VAPI_OK; fail: - vl_client_disconnect (); + vapi_client_disconnect (ctx); vl_client_api_unmap (); return rv; } +vapi_error_e +vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix, + int max_outstanding_requests, int response_queue_size, + vapi_mode_e mode, bool handle_keepalives) +{ + return vapi_connect_ex (ctx, name, chroot_prefix, max_outstanding_requests, + response_queue_size, mode, handle_keepalives, false); +} + /* * API client running in the same process as VPP */ @@ -607,6 +1107,11 @@ vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name, { int rv; + if (ctx->use_uds) + { + return VAPI_ENOTSUP; + } + if (response_queue_size <= 0 || max_outstanding_requests <= 0) { return VAPI_EINVAL; @@ -625,8 +1130,8 @@ vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name, ctx->requests_start = ctx->requests_count = 0; VAPI_DBG ("connect client `%s'", name); - if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size, - handle_keepalives) < 0) + if (vapi_shm_client_connect (ctx, (char *) name, 0, response_queue_size, + handle_keepalives) < 0) { return VAPI_ECON_FAIL; } @@ -689,7 +1194,7 @@ vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name, } return VAPI_OK; fail: - vl_client_disconnect (); + vapi_client_disconnect (ctx); return rv; } @@ -700,11 +1205,17 @@ vapi_disconnect_from_vpp (vapi_ctx_t ctx) { return VAPI_EINVAL; } + + if (ctx->use_uds) + { + return VAPI_ENOTSUP; + } + vl_api_memclnt_delete_reply_t *rp; svm_queue_t *vl_input_queue; time_t begin; vl_input_queue = ctx->vl_input_queue; - vl_client_send_disconnect (0 /* wait for reply */); + vapi_shm_client_send_disconnect (ctx, 0 /* wait for reply */); /* * Have to be careful here, in case the client is disconnecting @@ -748,19 +1259,14 @@ fail: return rv; } -vapi_error_e -vapi_disconnect (vapi_ctx_t ctx) +static vapi_error_e +vapi_shm_disconnect (vapi_ctx_t ctx) { - if (!ctx->connected) - { - return VAPI_EINVAL; - } - vl_api_memclnt_delete_reply_t *rp; svm_queue_t *vl_input_queue; time_t begin; vl_input_queue = ctx->vl_input_queue; - vl_client_send_disconnect (0 /* wait for reply */); + vapi_shm_client_send_disconnect (ctx, 0 /* wait for reply */); /* * Have to be careful here, in case the client is disconnecting @@ -808,90 +1314,162 @@ fail: return rv; } +static vapi_error_e +vapi_sock_disconnect (vapi_ctx_t ctx) +{ + vl_api_sockclnt_delete_reply_t *rp; + time_t begin; + u8 *msg = 0; + + vapi_sock_client_send_disconnect (ctx); + + begin = time (0); + vapi_error_e rv = VAPI_OK; + while (1) + { + time_t now; + + now = time (0); + + if (now >= (begin + 2)) + { + clib_warning ("peer unresponsive, give up"); + ctx->my_client_index = ~0; + rv = VAPI_ENORESP; + goto fail; + } + if (vapi_sock_recv_internal (ctx, &msg, 0) < 0) + continue; + + if (vec_len (msg) == 0) + continue; + + rp = (void *) msg; + + /* drain the queue */ + if (ntohs (rp->_vl_msg_id) != VL_API_SOCKCLNT_DELETE_REPLY) + { + clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id)); + continue; + } + vapi_sockclnt_delete_reply_t_handler ( + ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/); + break; + } +fail: + clib_socket_close (&ctx->client_socket); + vapi_api_name_and_crc_free (ctx); + + ctx->connected = false; + return rv; +} + vapi_error_e -vapi_get_fd (vapi_ctx_t ctx, int *fd) +vapi_disconnect (vapi_ctx_t ctx) { - return VAPI_ENOTSUP; + if (!ctx->connected) + { + return VAPI_EINVAL; + } + + if (ctx->use_uds) + { + return vapi_sock_disconnect (ctx); + } + return vapi_shm_disconnect (ctx); } vapi_error_e -vapi_send (vapi_ctx_t ctx, void *msg) +vapi_get_fd (vapi_ctx_t ctx, int *fd) { - vapi_error_e rv = VAPI_OK; - if (!ctx || !msg || !ctx->connected) + if (ctx->use_uds && fd) { - rv = VAPI_EINVAL; - goto out; + *fd = ctx->client_socket.fd; + return VAPI_OK; } - int tmp; - svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue; + return VAPI_ENOTSUP; +} + #if VAPI_DEBUG +static void +vapi_debug_log (vapi_ctx_t ctx, void *msg, const char *fun) +{ unsigned msgid = be16toh (*(u16 *) msg); if (msgid <= ctx->vl_msg_id_max) { vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid]; if (id < __vapi_metadata.count) { - VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid, + VAPI_DBG ("%s msg@%p:%u[%s]", fun, msg, msgid, __vapi_metadata.msgs[id]->name); } else { - VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid); + VAPI_DBG ("%s msg@%p:%u[UNKNOWN]", fun, msg, msgid); } } else { - VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid); + VAPI_DBG ("%s msg@%p:%u[UNKNOWN]", fun, msg, msgid); } +} +#endif + +static vapi_error_e +vapi_shm_send (vapi_ctx_t ctx, void *msg) +{ + int rv = VAPI_OK; + int tmp; + svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue; +#if VAPI_DEBUG + vapi_debug_log (ctx, msg, "send"); #endif - tmp = svm_queue_add (q, (u8 *) & msg, - VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1); + tmp = + svm_queue_add (q, (u8 *) &msg, VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1); if (tmp < 0) { rv = VAPI_EAGAIN; } else VL_MSG_API_POISON (msg); -out: - VAPI_DBG ("vapi_send() rv = %d", rv); + return rv; } vapi_error_e -vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2) +vapi_send (vapi_ctx_t ctx, void *msg) { vapi_error_e rv = VAPI_OK; - if (!ctx || !msg1 || !msg2 || !ctx->connected) + if (!ctx || !msg || !ctx->connected) { rv = VAPI_EINVAL; goto out; } - svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue; -#if VAPI_DEBUG - unsigned msgid1 = be16toh (*(u16 *) msg1); - unsigned msgid2 = be16toh (*(u16 *) msg2); - const char *name1 = "UNKNOWN"; - const char *name2 = "UNKNOWN"; - if (msgid1 <= ctx->vl_msg_id_max) + + if (ctx->use_uds) { - vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid1]; - if (id < __vapi_metadata.count) - { - name1 = __vapi_metadata.msgs[id]->name; - } + rv = vapi_sock_send (ctx, msg); } - if (msgid2 <= ctx->vl_msg_id_max) + else { - vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid2]; - if (id < __vapi_metadata.count) - { - name2 = __vapi_metadata.msgs[id]->name; - } + rv = vapi_shm_send (ctx, msg); } - VAPI_DBG ("send two: %u[%s], %u[%s]", msgid1, name1, msgid2, name2); + +out: + VAPI_DBG ("vapi_send() rv = %d", rv); + return rv; +} + +static vapi_error_e +vapi_shm_send2 (vapi_ctx_t ctx, void *msg1, void *msg2) +{ + vapi_error_e rv = VAPI_OK; + svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue; +#if VAPI_DEBUG + vapi_debug_log (ctx, msg1, "send2"); + vapi_debug_log (ctx, msg2, "send2"); #endif - int tmp = svm_queue_add2 (q, (u8 *) & msg1, (u8 *) & msg2, + int tmp = svm_queue_add2 (q, (u8 *) &msg1, (u8 *) &msg2, VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1); if (tmp < 0) { @@ -899,31 +1477,52 @@ vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2) } else VL_MSG_API_POISON (msg1); -out: - VAPI_DBG ("vapi_send() rv = %d", rv); + return rv; } vapi_error_e -vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size, - svm_q_conditional_wait_t cond, u32 time) +vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2) { - if (!ctx || !ctx->connected || !msg || !msg_size) + vapi_error_e rv = VAPI_OK; + if (!ctx || !msg1 || !msg2 || !ctx->connected) { - return VAPI_EINVAL; + rv = VAPI_EINVAL; + goto out; } + + if (ctx->use_uds) + { + rv = vapi_sock_send2 (ctx, msg1, msg2); + } + else + { + rv = vapi_shm_send2 (ctx, msg1, msg2); + } + +out: + VAPI_DBG ("vapi_send() rv = %d", rv); + return rv; +} + +static vapi_error_e +vapi_shm_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size, + svm_q_conditional_wait_t cond, u32 time) +{ vapi_error_e rv = VAPI_OK; uword data; svm_queue_t *q = ctx->vl_input_queue; -again: VAPI_DBG ("doing shm queue sub"); int tmp = svm_queue_sub (q, (u8 *) & data, cond, time); - if (tmp == 0) + if (tmp != 0) { + return VAPI_EAGAIN; + } + VL_MSG_API_UNPOISON ((void *) data); #if VAPI_DEBUG_ALLOC vapi_add_to_be_freed ((void *) data); @@ -937,61 +1536,99 @@ again: } *msg = (u8 *) data; *msg_size = ntohl (msgbuf->data_len); + #if VAPI_DEBUG - unsigned msgid = be16toh (*(u16 *) * msg); - if (msgid <= ctx->vl_msg_id_max) - { - vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid]; - if (id < __vapi_metadata.count) - { - VAPI_DBG ("recv msg@%p:%u[%s]", *msg, msgid, - __vapi_metadata.msgs[id]->name); - } - else - { - VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid); - } - } - else - { - VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid); - } + vapi_debug_log (ctx, msg, "recv"); #endif - if (ctx->handle_keepalives) + + return rv; +} + +static vapi_error_e +vapi_sock_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size, u32 time) +{ + vapi_error_e rv = VAPI_OK; + u8 *data = 0; + if (time == 0 && ctx->mode == VAPI_MODE_BLOCKING) + time = 1; + + rv = vapi_sock_recv_internal (ctx, &data, time); + + if (rv != VAPI_OK) + { + return rv; + } + + *msg = data; + *msg_size = vec_len (data); + +#if VAPI_DEBUG + vapi_debug_log (ctx, msg, "recv"); +#endif + + return rv; +} + +vapi_error_e +vapi_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size, + svm_q_conditional_wait_t cond, u32 time) +{ + if (!ctx || !ctx->connected || !msg || !msg_size) + { + return VAPI_EINVAL; + } + vapi_error_e rv = VAPI_OK; + +again: + if (ctx->use_uds) + { + rv = vapi_sock_recv (ctx, msg, msg_size, time); + } + else + { + rv = vapi_shm_recv (ctx, msg, msg_size, cond, time); + } + + if (rv != VAPI_OK) + return rv; + + if (ctx->handle_keepalives) + { + unsigned msgid = be16toh (*(u16 *) *msg); + if (msgid == vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive)) { - unsigned msgid = be16toh (*(u16 *) * msg); - if (msgid == - vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive)) + vapi_msg_memclnt_keepalive_reply *reply = NULL; + do { - vapi_msg_memclnt_keepalive_reply *reply = NULL; - do - { - reply = vapi_msg_alloc (ctx, sizeof (*reply)); - } - while (!reply); - reply->header.context = vapi_get_client_index (ctx); - reply->header._vl_msg_id = - vapi_lookup_vl_msg_id (ctx, - vapi_msg_id_memclnt_keepalive_reply); - reply->payload.retval = 0; - vapi_msg_memclnt_keepalive_reply_hton (reply); - while (VAPI_EAGAIN == vapi_send (ctx, reply)); - vapi_msg_free (ctx, *msg); - goto again; + reply = vapi_msg_alloc (ctx, sizeof (*reply)); } + while (!reply); + reply->header.context = vapi_get_client_index (ctx); + reply->header._vl_msg_id = + vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive_reply); + reply->payload.retval = 0; + vapi_msg_memclnt_keepalive_reply_hton (reply); + while (VAPI_EAGAIN == vapi_send (ctx, reply)) + ; + vapi_msg_free (ctx, *msg); + goto again; } } - else - { - rv = VAPI_EAGAIN; - } + return rv; } vapi_error_e -vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode) +vapi_wait (vapi_ctx_t ctx) { - return VAPI_ENOTSUP; + if (ctx->use_uds) + return VAPI_ENOTSUP; + + svm_queue_lock (ctx->vl_input_queue); + svm_queue_wait (ctx->vl_input_queue); + svm_queue_unlock (ctx->vl_input_queue); + + return VAPI_OK; } static vapi_error_e @@ -1042,8 +1679,34 @@ vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id, int payload_offset = vapi_get_payload_offset (id); void *payload = ((u8 *) msg) + payload_offset; bool is_last = true; - if (ctx->requests[tmp].is_dump) + switch (ctx->requests[tmp].type) { + case VAPI_REQUEST_STREAM: + if (ctx->requests[tmp].response_id == id) + { + is_last = false; + } + else + { + VAPI_DBG ("Stream response ID doesn't match current ID, move to " + "next ID"); + clib_memset (&ctx->requests[tmp], 0, + sizeof (ctx->requests[tmp])); + ++ctx->requests_start; + --ctx->requests_count; + if (ctx->requests_start == ctx->requests_size) + { + ctx->requests_start = 0; + } + tmp = ctx->requests_start; + if (ctx->requests[tmp].context != context) + { + VAPI_ERR ("Unexpected context %u, expected context %u!", + ctx->requests[tmp].context, context); + } + } + break; + case VAPI_REQUEST_DUMP: if (vapi_msg_id_control_ping_reply == id) { payload = NULL; @@ -1052,6 +1715,9 @@ vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id, { is_last = false; } + break; + case VAPI_REQUEST_REG: + break; } if (payload_offset != -1) { @@ -1155,13 +1821,13 @@ vapi_dispatch_one (vapi_ctx_t ctx) return VAPI_EINVAL; } const vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[vpp_id]; + vapi_get_swap_to_host_func (id) (msg); if (vapi_verify_msg_size (id, msg, size)) { vapi_msg_free (ctx, msg); return VAPI_EINVAL; } u32 context; - vapi_get_swap_to_host_func (id) (msg); if (vapi_msg_is_with_context (id)) { context = *(u32 *) (((u8 *) msg) + vapi_get_context_offset (id)); @@ -1346,6 +2012,16 @@ vapi_get_msg_name (vapi_msg_id_t id) return __vapi_metadata.msgs[id]->name; } +void +vapi_stop_rx_thread (vapi_ctx_t ctx) +{ + if (!ctx || !ctx->connected || !ctx->vl_input_queue) + { + return; + } + + vl_client_stop_rx_thread (ctx->vl_input_queue); +} /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vpp-api/vapi/vapi.h b/src/vpp-api/vapi/vapi.h index 46666293e4b..970c5080667 100644 --- a/src/vpp-api/vapi/vapi.h +++ b/src/vpp-api/vapi/vapi.h @@ -108,6 +108,25 @@ vapi_error_e vapi_connect (vapi_ctx_t ctx, const char *name, bool handle_keepalives); /** + * @brief connect to vpp + * + * @param ctx opaque vapi context, must be allocated using vapi_ctx_alloc first + * @param name application name + * @param path shared memory prefix or path to unix socket + * @param max_outstanding_requests max number of outstanding requests queued + * @param response_queue_size size of the response queue + * @param mode mode of operation - blocking or nonblocking + * @param handle_keepalives - if true, automatically handle memclnt_keepalive + * @param use_uds - if true, use unix domain socket transport + * + * @return VAPI_OK on success, other error code on error + */ +vapi_error_e vapi_connect_ex (vapi_ctx_t ctx, const char *name, + const char *path, int max_outstanding_requests, + int response_queue_size, vapi_mode_e mode, + bool handle_keepalives, bool use_uds); + +/** * @brief connect to vpp from a client in same process * @remark This MUST be called from a separate thread. If called * from the main thread, it will deadlock. @@ -175,7 +194,7 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg); * * @return VAPI_OK on success, other error code on error */ - vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2); +vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2); /** * @brief low-level api for reading messages from vpp @@ -191,25 +210,24 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg); * * @return VAPI_OK on success, other error code on error */ - vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size, - svm_q_conditional_wait_t cond, u32 time); +vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size, + svm_q_conditional_wait_t cond, u32 time); /** - * @brief wait for connection to become readable or writable + * @brief wait for connection to become readable * * @param ctx opaque vapi context - * @param mode type of property to wait for - readability, writability or both * * @return VAPI_OK on success, other error code on error */ - vapi_error_e vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode); +vapi_error_e vapi_wait (vapi_ctx_t ctx); /** * @brief pick next message sent by vpp and call the appropriate callback * * @return VAPI_OK on success, other error code on error */ - vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx); +vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx); /** * @brief loop vapi_dispatch_one until responses to all currently outstanding @@ -225,11 +243,11 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg); * * @return VAPI_OK on success, other error code on error */ - vapi_error_e vapi_dispatch (vapi_ctx_t ctx); +vapi_error_e vapi_dispatch (vapi_ctx_t ctx); /** generic vapi event callback */ - typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx, - void *payload); +typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx, + void *payload); /** * @brief set event callback to call when message with given id is dispatched @@ -239,8 +257,8 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg); * @param callback callback * @param callback_ctx context pointer stored and passed to callback */ - void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id, - vapi_event_cb callback, void *callback_ctx); +void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id, + vapi_event_cb callback, void *callback_ctx); /** * @brief clear event callback for given message id @@ -248,12 +266,12 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg); * @param ctx opaque vapi context * @param id message id */ - void vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id); +void vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id); /** generic vapi event callback */ - typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx, - void *callback_ctx, - vapi_msg_id_t id, void *msg); +typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx, + void *callback_ctx, + vapi_msg_id_t id, void *msg); /** * @brief set generic event callback * @@ -264,16 +282,29 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg); * @param callback callback * @param callback_ctx context pointer stored and passed to callback */ - void vapi_set_generic_event_cb (vapi_ctx_t ctx, - vapi_generic_event_cb callback, - void *callback_ctx); +void vapi_set_generic_event_cb (vapi_ctx_t ctx, vapi_generic_event_cb callback, + void *callback_ctx); /** * @brief clear generic event callback * * @param ctx opaque vapi context */ - void vapi_clear_generic_event_cb (vapi_ctx_t ctx); +void vapi_clear_generic_event_cb (vapi_ctx_t ctx); + +/** + * @brief signal RX thread to exit + * + * @note This adds a message to the client input queue that indicates that + * an RX thread should stop processing incoming messages and exit. If an + * application has an RX thread which sleeps while waiting for incoming + * messages using vapi_wait(), this call will allow the application to + * wake up from the vapi_wait() call and figure out that it should stop + * running. + * + * @param ctx opaque vapi context + */ +void vapi_stop_rx_thread (vapi_ctx_t ctx); #ifdef __cplusplus } diff --git a/src/vpp-api/vapi/vapi.hpp b/src/vpp-api/vapi/vapi.hpp index a1e33a93fd4..34d8f97ad89 100644 --- a/src/vpp-api/vapi/vapi.hpp +++ b/src/vpp-api/vapi/vapi.hpp @@ -140,6 +140,10 @@ private: template <typename Req, typename Resp, typename... Args> friend class Dump; + template <typename Req, typename Resp, typename StreamMessage, + typename... Args> + friend class Stream; + template <typename M> friend class Event_registration; }; @@ -199,13 +203,14 @@ public: * * @return VAPI_OK on success, other error code on error */ - vapi_error_e connect (const char *name, const char *chroot_prefix, - int max_outstanding_requests, int response_queue_size, - bool handle_keepalives = true) + vapi_error_e + connect (const char *name, const char *chroot_prefix, + int max_outstanding_requests, int response_queue_size, + bool handle_keepalives = true, bool use_uds = false) { - return vapi_connect (vapi_ctx, name, chroot_prefix, - max_outstanding_requests, response_queue_size, - VAPI_MODE_BLOCKING, handle_keepalives); + return vapi_connect_ex (vapi_ctx, name, chroot_prefix, + max_outstanding_requests, response_queue_size, + VAPI_MODE_BLOCKING, handle_keepalives, use_uds); } /** @@ -417,7 +422,7 @@ private: void unregister_request (Common_req *request) { std::lock_guard<std::recursive_mutex> lock (requests_mutex); - std::remove (requests.begin (), requests.end (), request); + requests.erase (std::remove (requests.begin (), requests.end (), request)); } template <typename M> void register_event (Event_registration<M> *event) @@ -451,6 +456,10 @@ private: template <typename Req, typename Resp, typename... Args> friend class Dump; + template <typename Req, typename Resp, typename StreamMessage, + typename... Args> + friend class Stream; + template <typename M> friend class Result_set; template <typename M> friend class Event_registration; @@ -497,6 +506,10 @@ template <typename Req, typename Resp, typename... Args> class Request; template <typename Req, typename Resp, typename... Args> class Dump; +template <typename Req, typename Resp, typename StreamMessage, + typename... Args> +class Stream; + template <class, class = void> struct vapi_has_payload_trait : std::false_type { }; @@ -627,6 +640,10 @@ private: template <typename Req, typename Resp, typename... Args> friend class Dump; + template <typename Req, typename Resp, typename StreamMessage, + typename... Args> + friend class Stream; + template <typename X> friend class Event_registration; template <typename X> friend class Result_set; @@ -644,10 +661,11 @@ class Request : public Common_req { public: Request (Connection &con, Args... args, - std::function<vapi_error_e (Request<Req, Resp, Args...> &)> - callback = nullptr) - : Common_req{con}, callback{callback}, - request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr} + std::function<vapi_error_e (Request<Req, Resp, Args...> &)> + callback = nullptr) + : Common_req{ con }, callback{ std::move (callback) }, + request{ con, vapi_alloc<Req> (con, args...) }, response{ con, + nullptr } { } @@ -772,12 +790,96 @@ private: bool complete; std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set; + template <typename Req, typename Resp, typename StreamMessage, + typename... Args> + friend class Stream; + template <typename Req, typename Resp, typename... Args> friend class Dump; template <typename X> friend class Event_registration; }; /** + * Class representing a RPC request - zero or more identical responses to a + * single request message with a response + */ +template <typename Req, typename Resp, typename StreamMessage, + typename... Args> +class Stream : public Common_req +{ +public: + Stream ( + Connection &con, Args... args, + std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)> + cb = nullptr) + : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) }, + response{ con, nullptr }, result_set{ con }, callback{ std::move (cb) } + { + } + + Stream (const Stream &) = delete; + + virtual ~Stream () {} + + virtual std::tuple<vapi_error_e, bool> + assign_response (vapi_msg_id_t id, void *shm_data) + { + if (id == response.get_msg_id ()) + { + response.assign_response (id, shm_data); + result_set.mark_complete (); + set_response_state (RESPONSE_READY); + if (nullptr != callback) + { + return std::make_pair (callback (*this), true); + } + return std::make_pair (VAPI_OK, true); + } + else + { + result_set.assign_response (id, shm_data); + } + return std::make_pair (VAPI_OK, false); + } + + vapi_error_e + execute () + { + return con.send (this); + } + + const Msg<Req> & + get_request (void) + { + return request; + } + + const Msg<Resp> & + get_response (void) + { + return response; + } + + using resp_type = typename Msg<StreamMessage>::shm_data_type; + + const Result_set<StreamMessage> & + get_result_set (void) const + { + return result_set; + } + +private: + Msg<Req> request; + Msg<Resp> response; + Result_set<StreamMessage> result_set; + std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)> + callback; + + friend class Connection; + friend class Result_set<StreamMessage>; +}; + +/** * Class representing a dump request - zero or more identical responses to a * single request message */ @@ -786,10 +888,10 @@ class Dump : public Common_req { public: Dump (Connection &con, Args... args, - std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback = - nullptr) - : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)}, - result_set{con}, callback{callback} + std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback = + nullptr) + : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) }, + result_set{ con }, callback{ std::move (callback) } { } @@ -853,9 +955,9 @@ template <typename M> class Event_registration : public Common_req { public: Event_registration ( - Connection &con, - std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr) - : Common_req{con}, result_set{con}, callback{callback} + Connection &con, + std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr) + : Common_req{ con }, result_set{ con }, callback{ std::move (callback) } { if (!con.is_msg_available (M::get_msg_id ())) { diff --git a/src/vpp-api/vapi/vapi_c_gen.py b/src/vpp-api/vapi/vapi_c_gen.py index 2978ebd2077..9d1efb5e438 100755 --- a/src/vpp-api/vapi/vapi_c_gen.py +++ b/src/vpp-api/vapi/vapi_c_gen.py @@ -23,7 +23,7 @@ class CField(Field): return "vapi_type_%s" % self.name def get_c_def(self): - if self.type.get_c_name() == "vl_api_string_t": + if self.type.get_c_name() == "string": if self.len: return "u8 %s[%d];" % (self.name, self.len) else: @@ -85,12 +85,15 @@ class CField(Field): def needs_byte_swap(self): return self.type.needs_byte_swap() - def get_vla_field_length_name(self, path): + def get_vla_parameter_name(self, path): return "%s_%s_array_size" % ("_".join(path), self.name) + def get_vla_field_name(self, path): + return ".".join(path + [self.nelem_field.name]) + def get_alloc_vla_param_names(self, path): if self.is_vla(): - result = [self.get_vla_field_length_name(path)] + result = [self.get_vla_parameter_name(path)] else: result = [] if self.type.has_vla(): @@ -98,20 +101,24 @@ class CField(Field): result.extend(t) return result - def get_vla_calc_size_code(self, prefix, path): + def get_vla_calc_size_code(self, prefix, path, is_alloc): if self.is_vla(): result = [ "sizeof(%s.%s[0]) * %s" % ( ".".join([prefix] + path), self.name, - self.get_vla_field_length_name(path), + ( + self.get_vla_parameter_name(path) + if is_alloc + else "%s.%s" % (prefix, self.get_vla_field_name(path)) + ), ) ] else: result = [] if self.type.has_vla(): - t = self.type.get_vla_calc_size_code(prefix, path + [self.name]) + t = self.type.get_vla_calc_size_code(prefix, path + [self.name], is_alloc) result.extend(t) return result @@ -123,7 +130,7 @@ class CField(Field): % ( ".".join([prefix] + path), self.nelem_field.name, - self.get_vla_field_length_name(path), + self.get_vla_parameter_name(path), ) ) if self.type.has_vla(): @@ -173,17 +180,16 @@ class CStruct(Struct): for x in f.get_alloc_vla_param_names(path) ] - def get_vla_calc_size_code(self, prefix, path): + def get_vla_calc_size_code(self, prefix, path, is_alloc): return [ x for f in self.fields if f.has_vla() - for x in f.get_vla_calc_size_code(prefix, path) + for x in f.get_vla_calc_size_code(prefix, path, is_alloc) ] class CSimpleType(SimpleType): - swap_to_be_dict = { "i16": "htobe16", "u16": "htobe16", @@ -289,6 +295,8 @@ class CUnion(Union): class CStructType(StructType, CStruct): def get_c_name(self): + if self.name == "vl_api_string_t": + return "vl_api_string_t" return "vapi_type_%s" % self.name def get_swap_to_be_func_name(self): @@ -399,7 +407,9 @@ class CMessage(Message): " + %s" % x for f in self.fields if f.has_vla() - for x in f.get_vla_calc_size_code("msg->payload", []) + for x in f.get_vla_calc_size_code( + "msg->payload", [], is_alloc=True + ) ] ), ), @@ -443,10 +453,12 @@ class CMessage(Message): " return sizeof(*msg)%s;" % "".join( [ - "+ msg->payload.%s * sizeof(msg->payload.%s[0])" - % (f.nelem_field.name, f.name) + " + %s" % x for f in self.fields - if f.nelem_field is not None + if f.has_vla() + for x in f.get_vla_calc_size_code( + "msg->payload", [], is_alloc=False + ) ] ), "}", @@ -478,7 +490,7 @@ class CMessage(Message): {{ VAPI_ERR("Truncated '{self.name}' msg received, received %lu" "bytes, expected %lu bytes.", buf_size, - sizeof({self.get_calc_msg_size_func_name()})); + {self.get_calc_msg_size_func_name()}(msg)); return -1; }} return 0; @@ -583,12 +595,16 @@ class CMessage(Message): ' VAPI_DBG("Swapping `%s\'@%%p to big endian", msg);' % self.get_c_name() ), - " %s(&msg->header);" % self.header.get_swap_to_be_func_name() - if self.header is not None - else "", - " %s(&msg->payload);" % self.get_swap_payload_to_be_func_name() - if self.has_payload() - else "", + ( + " %s(&msg->header);" % self.header.get_swap_to_be_func_name() + if self.header is not None + else "" + ), + ( + " %s(&msg->payload);" % self.get_swap_payload_to_be_func_name() + if self.has_payload() + else "" + ), "}", ] ) @@ -602,12 +618,16 @@ class CMessage(Message): ' VAPI_DBG("Swapping `%s\'@%%p to host byte order", msg);' % self.get_c_name() ), - " %s(&msg->header);" % self.header.get_swap_to_host_func_name() - if self.header is not None - else "", - " %s(&msg->payload);" % self.get_swap_payload_to_host_func_name() - if self.has_payload() - else "", + ( + " %s(&msg->header);" % self.header.get_swap_to_host_func_name() + if self.header is not None + else "" + ), + ( + " %s(&msg->payload);" % self.get_swap_payload_to_host_func_name() + if self.has_payload() + else "" + ), "}", ] ) @@ -616,45 +636,66 @@ class CMessage(Message): return "vapi_%s" % self.name def get_op_func_decl(self): - if self.reply.has_payload(): - return "vapi_error_e %s(%s)" % ( - self.get_op_func_name(), - ",\n ".join( - [ - "struct vapi_ctx_s *ctx", - "%s *msg" % self.get_c_name(), - "vapi_error_e (*callback)(struct vapi_ctx_s *ctx", - " void *callback_ctx", - " vapi_error_e rv", - " bool is_last", - " %s *reply)" - % self.reply.get_payload_struct_name(), - "void *callback_ctx", - ] - ), - ) - else: - return "vapi_error_e %s(%s)" % ( - self.get_op_func_name(), - ",\n ".join( - [ - "struct vapi_ctx_s *ctx", - "%s *msg" % self.get_c_name(), - "vapi_error_e (*callback)(struct vapi_ctx_s *ctx", - " void *callback_ctx", - " vapi_error_e rv", - " bool is_last)", - "void *callback_ctx", - ] - ), - ) + stream_param_lines = [] + if self.has_stream_msg: + stream_param_lines = [ + "vapi_error_e (*details_callback)(struct vapi_ctx_s *ctx", + " void *callback_ctx", + " vapi_error_e rv", + " bool is_last", + " %s *details)" + % self.stream_msg.get_payload_struct_name(), + "void *details_callback_ctx", + ] + + return "vapi_error_e %s(%s)" % ( + self.get_op_func_name(), + ",\n ".join( + [ + "struct vapi_ctx_s *ctx", + "%s *msg" % self.get_c_name(), + "vapi_error_e (*reply_callback)(struct vapi_ctx_s *ctx", + " void *callback_ctx", + " vapi_error_e rv", + " bool is_last", + " %s *reply)" + % self.reply.get_payload_struct_name(), + ] + + [ + "void *reply_callback_ctx", + ] + + stream_param_lines + ), + ) def get_op_func_def(self): + param_check_lines = [" if (!msg || !reply_callback) {"] + store_request_lines = [ + " vapi_store_request(ctx, req_context, %s, %s, " + % ( + self.reply.get_msg_id_name(), + "VAPI_REQUEST_DUMP" if self.reply_is_stream else "VAPI_REQUEST_REG", + ), + " (vapi_cb_t)reply_callback, reply_callback_ctx);", + ] + if self.has_stream_msg: + param_check_lines = [ + " if (!msg || !reply_callback || !details_callback) {" + ] + store_request_lines = [ + f" vapi_store_request(ctx, req_context, {self.stream_msg.get_msg_id_name()}, VAPI_REQUEST_STREAM, ", + " (vapi_cb_t)details_callback, details_callback_ctx);", + f" vapi_store_request(ctx, req_context, {self.reply.get_msg_id_name()}, VAPI_REQUEST_REG, ", + " (vapi_cb_t)reply_callback, reply_callback_ctx);", + ] + return "\n".join( [ "%s" % self.get_op_func_decl(), "{", - " if (!msg || !callback) {", + ] + + param_check_lines + + [ " return VAPI_EINVAL;", " }", " if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {", @@ -670,14 +711,12 @@ class CMessage(Message): ( " if (VAPI_OK == (rv = vapi_send_with_control_ping " "(ctx, msg, req_context))) {" - if self.reply_is_stream + if (self.reply_is_stream and not self.has_stream_msg) else " if (VAPI_OK == (rv = vapi_send (ctx, msg))) {" ), - ( - " vapi_store_request(ctx, req_context, %s, " - "(vapi_cb_t)callback, callback_ctx);" - % ("true" if self.reply_is_stream else "false") - ), + ] + + store_request_lines + + [ " if (VAPI_OK != vapi_producer_unlock (ctx)) {", " abort (); /* this really shouldn't happen */", " }", @@ -762,12 +801,16 @@ class CMessage(Message): " name_with_crc,", " sizeof(name_with_crc) - 1,", " true," if has_context else " false,", - " offsetof(%s, context)," % self.header.get_c_name() - if has_context - else " 0,", - (" offsetof(%s, payload)," % self.get_c_name()) - if self.has_payload() - else " VAPI_INVALID_MSG_ID,", + ( + " offsetof(%s, context)," % self.header.get_c_name() + if has_context + else " 0," + ), + ( + (" offsetof(%s, payload)," % self.get_c_name()) + if self.has_payload() + else " VAPI_INVALID_MSG_ID," + ), " (verify_msg_size_fn_t)%s," % self.get_verify_msg_size_func_name(), " (generic_swap_fn_t)%s," % self.get_swap_to_be_func_name(), " (generic_swap_fn_t)%s," % self.get_swap_to_host_func_name(), @@ -793,6 +836,8 @@ def emit_definition(parser, json_file, emitted, o): emit_definition(parser, json_file, emitted, x) if hasattr(o, "reply"): emit_definition(parser, json_file, emitted, o.reply) + if hasattr(o, "stream_msg"): + emit_definition(parser, json_file, emitted, o.stream_msg) if hasattr(o, "get_c_def"): if ( o not in parser.enums_by_json[json_file] @@ -821,14 +866,14 @@ def emit_definition(parser, json_file, emitted, o): print("%s%s" % (function_attrs, o.get_calc_msg_size_func_def())) print("") print("%s%s" % (function_attrs, o.get_verify_msg_size_func_def())) - if not o.is_reply and not o.is_event: + if not o.is_reply and not o.is_event and not o.is_stream: print("") print("%s%s" % (function_attrs, o.get_alloc_func_def())) print("") print("%s%s" % (function_attrs, o.get_op_func_def())) print("") print("%s" % o.get_c_constructor()) - if o.is_reply or o.is_event: + if (o.is_reply or o.is_event) and not o.is_stream: print("") print("%s%s;" % (function_attrs, o.get_event_cb_func_def())) elif hasattr(o, "get_swap_to_be_func_def"): @@ -865,6 +910,20 @@ def gen_json_unified_header(parser, logger, j, io, name): print("#ifdef __cplusplus") print('extern "C" {') print("#endif") + + print("#ifndef __vl_api_string_swap_fns_defined__") + print("#define __vl_api_string_swap_fns_defined__") + print("") + print("#include <vlibapi/api_types.h>") + print("") + function_attrs = "static inline " + o = parser.types["vl_api_string_t"] + print("%s%s" % (function_attrs, o.get_swap_to_be_func_def())) + print("") + print("%s%s" % (function_attrs, o.get_swap_to_host_func_def())) + print("") + print("#endif //__vl_api_string_swap_fns_defined__") + if name == "memclnt.api.vapi.h": print("") print( diff --git a/src/vpp-api/vapi/vapi_c_test.c b/src/vpp-api/vapi/vapi_c_test.c index 99a93fb22fd..7a0e462e40a 100644 --- a/src/vpp-api/vapi/vapi_c_test.c +++ b/src/vpp-api/vapi/vapi_c_test.c @@ -28,6 +28,7 @@ #include <vapi/vlib.api.vapi.h> #include <vapi/vpe.api.vapi.h> #include <vapi/interface.api.vapi.h> +#include <vapi/mss_clamp.api.vapi.h> #include <vapi/l2.api.vapi.h> #include <fake.api.vapi.h> @@ -36,11 +37,13 @@ DEFINE_VAPI_MSG_IDS_VPE_API_JSON; DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON; +DEFINE_VAPI_MSG_IDS_MSS_CLAMP_API_JSON; DEFINE_VAPI_MSG_IDS_L2_API_JSON; DEFINE_VAPI_MSG_IDS_FAKE_API_JSON; static char *app_name = NULL; static char *api_prefix = NULL; +static bool use_uds = false; static const int max_outstanding_requests = 64; static const int response_queue_size = 32; @@ -63,8 +66,9 @@ START_TEST (test_invalid_values) ck_assert_ptr_eq (NULL, sv); rv = vapi_send (ctx, sv); ck_assert_int_eq (VAPI_EINVAL, rv); - rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, - response_queue_size, VAPI_MODE_BLOCKING, true); + rv = + vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests, + response_queue_size, VAPI_MODE_BLOCKING, true, use_uds); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_send (ctx, NULL); ck_assert_int_eq (VAPI_EINVAL, rv); @@ -365,8 +369,9 @@ START_TEST (test_connect) vapi_ctx_t ctx; vapi_error_e rv = vapi_ctx_alloc (&ctx); ck_assert_int_eq (VAPI_OK, rv); - rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, - response_queue_size, VAPI_MODE_BLOCKING, true); + rv = + vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests, + response_queue_size, VAPI_MODE_BLOCKING, true, use_uds); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_disconnect (ctx); ck_assert_int_eq (VAPI_OK, rv); @@ -382,8 +387,9 @@ setup_blocking (void) { vapi_error_e rv = vapi_ctx_alloc (&ctx); ck_assert_int_eq (VAPI_OK, rv); - rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, - response_queue_size, VAPI_MODE_BLOCKING, true); + rv = + vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests, + response_queue_size, VAPI_MODE_BLOCKING, true, use_uds); ck_assert_int_eq (VAPI_OK, rv); } @@ -392,8 +398,9 @@ setup_nonblocking (void) { vapi_error_e rv = vapi_ctx_alloc (&ctx); ck_assert_int_eq (VAPI_OK, rv); - rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, - response_queue_size, VAPI_MODE_NONBLOCKING, true); + rv = vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests, + response_queue_size, VAPI_MODE_NONBLOCKING, true, + use_uds); ck_assert_int_eq (VAPI_OK, rv); } @@ -481,6 +488,48 @@ sw_interface_dump_cb (struct vapi_ctx_s *ctx, void *callback_ctx, return VAPI_OK; } +vapi_error_e +vapi_mss_clamp_enable_disable_reply_cb ( + struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_mss_clamp_enable_disable_reply *reply) +{ + bool *x = callback_ctx; + *x = true; + return VAPI_OK; +} + +vapi_error_e +vapi_mss_clamp_get_reply_cb (struct vapi_ctx_s *ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_mss_clamp_get_reply *reply) +{ + int *counter = callback_ctx; + ck_assert_int_gt (*counter, 0); // make sure details were called first + ++*counter; + ck_assert_int_eq (is_last, true); + printf ("Got mss clamp reply error %d\n", rv); + ck_assert_int_eq (rv, VAPI_OK); + printf ("counter is %d", *counter); + return VAPI_OK; +} + +vapi_error_e +vapi_mss_clamp_get_details_cb (struct vapi_ctx_s *ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_mss_clamp_details *details) +{ + int *counter = callback_ctx; + ++*counter; + if (!is_last) + { + printf ("Got ipv4 mss clamp to %u for sw_if_index %u\n", + details->ipv4_mss, details->sw_if_index); + ck_assert_int_eq (details->ipv4_mss, 1000 + details->sw_if_index); + } + printf ("counter is %d", *counter); + return VAPI_OK; +} + START_TEST (test_loopbacks_1) { printf ("--- Create/delete loopbacks using blocking API ---\n"); @@ -521,6 +570,37 @@ START_TEST (test_loopbacks_1) mac_addresses[i][3], mac_addresses[i][4], mac_addresses[i][5], sw_if_indexes[i]); } + + { // new context + for (int i = 0; i < num_ifs; ++i) + { + vapi_msg_mss_clamp_enable_disable *mc = + vapi_alloc_mss_clamp_enable_disable (ctx); + mc->payload.sw_if_index = sw_if_indexes[i]; + mc->payload.ipv4_mss = 1000 + sw_if_indexes[i]; + mc->payload.ipv4_direction = MSS_CLAMP_DIR_RX; + bool reply_ctx = false; + printf ("Set ipv4 mss clamp to %u for sw_if_index %u\n", + mc->payload.ipv4_mss, mc->payload.sw_if_index); + vapi_error_e rv = vapi_mss_clamp_enable_disable ( + ctx, mc, vapi_mss_clamp_enable_disable_reply_cb, &reply_ctx); + ck_assert_int_eq (VAPI_OK, rv); + ck_assert_int_eq (reply_ctx, true); + } + } + + { // new context + int counter = 0; + vapi_msg_mss_clamp_get *msg = vapi_alloc_mss_clamp_get (ctx); + msg->payload.sw_if_index = ~0; + vapi_error_e rv = + vapi_mss_clamp_get (ctx, msg, vapi_mss_clamp_get_reply_cb, &counter, + vapi_mss_clamp_get_details_cb, &counter); + printf ("counter is %d", counter); + ck_assert_int_eq (VAPI_OK, rv); + ck_assert_int_eq (counter, num_ifs + 1); + } + bool seen[num_ifs]; sw_interface_dump_ctx dctx = { false, num_ifs, sw_if_indexes, seen, 0 }; vapi_msg_sw_interface_dump *dump; @@ -530,7 +610,7 @@ START_TEST (test_loopbacks_1) { dctx.last_called = false; clib_memset (&seen, 0, sizeof (seen)); - dump = vapi_alloc_sw_interface_dump (ctx); + dump = vapi_alloc_sw_interface_dump (ctx, 0); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, @@ -559,7 +639,7 @@ START_TEST (test_loopbacks_1) } dctx.last_called = false; clib_memset (&seen, 0, sizeof (seen)); - dump = vapi_alloc_sw_interface_dump (ctx); + dump = vapi_alloc_sw_interface_dump (ctx, 0); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) @@ -688,7 +768,7 @@ START_TEST (test_loopbacks_2) bool seen[num_ifs]; clib_memset (&seen, 0, sizeof (seen)); sw_interface_dump_ctx dctx = { false, num_ifs, sw_if_indexes, seen, 0 }; - vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx); + vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx, 0); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) @@ -728,7 +808,7 @@ START_TEST (test_loopbacks_2) } clib_memset (&seen, 0, sizeof (seen)); dctx.last_called = false; - dump = vapi_alloc_sw_interface_dump (ctx); + dump = vapi_alloc_sw_interface_dump (ctx, 0); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) @@ -847,7 +927,7 @@ START_TEST (test_no_response_2) { printf ("--- Simulate no response to dump message ---\n"); vapi_error_e rv; - vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx); + vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx, 0); dump->header._vl_msg_id = ~0; /* malformed ID causes vpp to drop the msg */ int no_called = 0; while (VAPI_EAGAIN == @@ -990,13 +1070,23 @@ test_suite (void) int main (int argc, char *argv[]) { - if (3 != argc) + if (4 != argc) { printf ("Invalid argc==`%d'\n", argc); return EXIT_FAILURE; } app_name = argv[1]; api_prefix = argv[2]; + if (!strcmp (argv[3], "shm")) + use_uds = 0; + else if (!strcmp (argv[3], "uds")) + use_uds = 1; + else + { + printf ("Unrecognised required argument '%s', expected 'uds' or 'shm'.", + argv[3]); + return EXIT_FAILURE; + } printf ("App name: `%s', API prefix: `%s'\n", app_name, api_prefix); int number_failed; diff --git a/src/vpp-api/vapi/vapi_common.h b/src/vpp-api/vapi/vapi_common.h index 7157f0a8e0d..69b9b788b51 100644 --- a/src/vpp-api/vapi/vapi_common.h +++ b/src/vpp-api/vapi/vapi_common.h @@ -22,37 +22,34 @@ extern "C" { #endif -typedef enum -{ - VAPI_OK = 0, /**< success */ - VAPI_EINVAL, /**< invalid value encountered */ - VAPI_EAGAIN, /**< operation would block */ - VAPI_ENOTSUP, /**< operation not supported */ - VAPI_ENOMEM, /**< out of memory */ - VAPI_ENORESP, /**< no response to request */ - VAPI_EMAP_FAIL, /**< failure while mapping api */ - VAPI_ECON_FAIL, /**< failure while connecting to vpp */ - VAPI_EINCOMPATIBLE, /**< fundamental incompatibility while connecting to vpp - (control ping/control ping reply mismatch) */ - VAPI_MUTEX_FAILURE, /**< failure manipulating internal mutex(es) */ - VAPI_EUSER, /**< user error used for breaking dispatch, - never used by VAPI */ -} vapi_error_e; - -typedef enum -{ - VAPI_MODE_BLOCKING = 1, /**< operations block until response received */ - VAPI_MODE_NONBLOCKING = 2, /**< operations never block */ -} vapi_mode_e; - -typedef enum -{ - VAPI_WAIT_FOR_READ, /**< wait until some message is readable */ - VAPI_WAIT_FOR_WRITE, /**< wait until a message can be written */ - VAPI_WAIT_FOR_READ_WRITE, /**< wait until a read or write can be done */ -} vapi_wait_mode_e; - -typedef unsigned int vapi_msg_id_t; + typedef enum + { + VAPI_OK = 0, /**< success */ + VAPI_EINVAL, /**< invalid value encountered */ + VAPI_EAGAIN, /**< operation would block */ + VAPI_ENOTSUP, /**< operation not supported */ + VAPI_ENOMEM, /**< out of memory */ + VAPI_ENORESP, /**< no response to request */ + VAPI_EMAP_FAIL, /**< failure while mapping api */ + VAPI_ECON_FAIL, /**< failure while connecting to vpp */ + VAPI_EINCOMPATIBLE, /**< fundamental incompatibility while connecting to + vpp (control ping/control ping reply mismatch) */ + VAPI_MUTEX_FAILURE, /**< failure manipulating internal mutex(es) */ + VAPI_EUSER, /**< user error used for breaking dispatch, + never used by VAPI */ + VAPI_ENOTSOCK, /**< vapi socket doesn't refer to a socket */ + VAPI_EACCES, /**< no write permission for socket */ + VAPI_ECONNRESET, /**< connection reset by peer*/ + VAPI_ESOCK_FAILURE, /**< generic socket failure, check errno */ + } vapi_error_e; + + typedef enum + { + VAPI_MODE_BLOCKING = 1, /**< operations block until response received */ + VAPI_MODE_NONBLOCKING = 2, /**< operations never block */ + } vapi_mode_e; + + typedef unsigned int vapi_msg_id_t; #define VAPI_INVALID_MSG_ID ((vapi_msg_id_t)(~0)) diff --git a/src/vpp-api/vapi/vapi_cpp_gen.py b/src/vpp-api/vapi/vapi_cpp_gen.py index 33744a3d58c..165730ca4b8 100755 --- a/src/vpp-api/vapi/vapi_cpp_gen.py +++ b/src/vpp-api/vapi/vapi_cpp_gen.py @@ -96,6 +96,13 @@ class CppMessage(CMessage): return "%s%s" % (self.name[0].upper(), self.name[1:]) def get_req_template_name(self): + if self.has_stream_msg: + return "Stream<%s, %s, %s>" % ( + self.get_c_name(), + self.reply.get_c_name(), + self.stream_msg.get_c_name(), + ) + if self.reply_is_stream: template = "Dump" else: @@ -196,7 +203,7 @@ def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments): print("/* m.get_cpp_constructor() */") print("%s" % m.get_cpp_constructor()) print("") - if not m.is_reply and not m.is_event: + if not m.is_reply and not m.is_event and not m.is_stream: if add_debug_comments: print("/* m.get_alloc_template_instantiation() */") print("%s" % m.get_alloc_template_instantiation()) @@ -210,6 +217,8 @@ def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments): print("/* m.get_reply_type_alias() */") print("%s" % m.get_reply_type_alias()) continue + if m.is_stream: + continue if add_debug_comments: print("/* m.get_req_template_instantiation() */") print("%s" % m.get_req_template_instantiation()) diff --git a/src/vpp-api/vapi/vapi_cpp_test.cpp b/src/vpp-api/vapi/vapi_cpp_test.cpp index c0e0cdc3ab8..918c7590b60 100644 --- a/src/vpp-api/vapi/vapi_cpp_test.cpp +++ b/src/vpp-api/vapi/vapi_cpp_test.cpp @@ -25,14 +25,17 @@ #include <vapi/vapi.hpp> #include <vapi/vpe.api.vapi.hpp> #include <vapi/interface.api.vapi.hpp> +#include <vapi/mss_clamp.api.vapi.hpp> #include <fake.api.vapi.hpp> DEFINE_VAPI_MSG_IDS_VPE_API_JSON; DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON; +DEFINE_VAPI_MSG_IDS_MSS_CLAMP_API_JSON; DEFINE_VAPI_MSG_IDS_FAKE_API_JSON; static char *app_name = nullptr; static char *api_prefix = nullptr; +static bool use_uds = false; static const int max_outstanding_requests = 32; static const int response_queue_size = 32; @@ -58,8 +61,9 @@ Connection con; void setup (void) { - vapi_error_e rv = con.connect ( - app_name, api_prefix, max_outstanding_requests, response_queue_size); + vapi_error_e rv = + con.connect (app_name, api_prefix, max_outstanding_requests, + response_queue_size, true, use_uds); ck_assert_int_eq (VAPI_OK, rv); } @@ -145,8 +149,53 @@ START_TEST (test_loopbacks_1) } { // new context + for (int i = 0; i < num_ifs; ++i) + { + Mss_clamp_enable_disable d (con); + auto &req = d.get_request ().get_payload (); + req.sw_if_index = sw_if_indexes[i]; + req.ipv4_mss = 1420; + req.ipv4_direction = vapi_enum_mss_clamp_dir::MSS_CLAMP_DIR_RX; + auto rv = d.execute (); + ck_assert_int_eq (VAPI_OK, rv); + WAIT_FOR_RESPONSE (d, rv); + ck_assert_int_eq (VAPI_OK, rv); + } + } + + { // new context + bool seen[num_ifs] = { 0 }; + Mss_clamp_get d (con); + d.get_request ().get_payload ().sw_if_index = ~0; + auto rv = d.execute (); + ck_assert_int_eq (VAPI_OK, rv); + WAIT_FOR_RESPONSE (d, rv); + ck_assert_int_eq (VAPI_OK, rv); + auto &rs = d.get_result_set (); + for (auto &r : rs) + { + auto &p = r.get_payload (); + ck_assert_int_eq (p.ipv4_mss, 1420); + printf ("tcp-clamp: sw_if_idx %u ip4-mss %d dir %d\n", p.sw_if_index, + p.ipv4_mss, p.ipv4_direction); + for (int i = 0; i < num_ifs; ++i) + { + if (sw_if_indexes[i] == p.sw_if_index) + { + ck_assert_int_eq (0, seen[i]); + seen[i] = true; + } + } + } + for (int i = 0; i < num_ifs; ++i) + { + ck_assert_int_eq (1, seen[i]); + } + } + + { // new context bool seen[num_ifs] = {0}; - Sw_interface_dump d (con); + Sw_interface_dump d (con, 0); auto rv = d.execute (); ck_assert_int_eq (VAPI_OK, rv); WAIT_FOR_RESPONSE (d, rv); @@ -185,7 +234,7 @@ START_TEST (test_loopbacks_1) } { // new context - Sw_interface_dump d (con); + Sw_interface_dump d (con, 0); auto rv = d.execute (); ck_assert_int_eq (VAPI_OK, rv); WAIT_FOR_RESPONSE (d, rv); @@ -300,7 +349,7 @@ START_TEST (test_loopbacks_2) } Sw_interface_dump_cb<num_ifs> swdcb (ccbs); - Sw_interface_dump d (con, std::ref (swdcb)); + Sw_interface_dump d (con, 0, std::ref (swdcb)); auto rv = d.execute (); ck_assert_int_eq (VAPI_OK, rv); WAIT_FOR_RESPONSE (d, rv); @@ -326,7 +375,7 @@ START_TEST (test_loopbacks_2) } { // new context - Sw_interface_dump d (con); + Sw_interface_dump d (con, 0); auto rv = d.execute (); ck_assert_int_eq (VAPI_OK, rv); WAIT_FOR_RESPONSE (d, rv); @@ -405,14 +454,25 @@ Suite *test_suite (void) int main (int argc, char *argv[]) { - if (3 != argc) + if (4 != argc) { printf ("Invalid argc==`%d'\n", argc); return EXIT_FAILURE; } app_name = argv[1]; api_prefix = argv[2]; - printf ("App name: `%s', API prefix: `%s'\n", app_name, api_prefix); + if (!strcmp (argv[3], "shm")) + use_uds = 0; + else if (!strcmp (argv[3], "uds")) + use_uds = 1; + else + { + printf ("Unrecognised required argument '%s', expected 'uds' or 'shm'.", + argv[3]); + return EXIT_FAILURE; + } + printf ("App name: `%s', API prefix: `%s', use unix sockets %d\n", app_name, + api_prefix, use_uds); int number_failed; Suite *s; diff --git a/src/vpp-api/vapi/vapi_internal.h b/src/vpp-api/vapi/vapi_internal.h index 49c041769d0..ca47dd10459 100644 --- a/src/vpp-api/vapi/vapi_internal.h +++ b/src/vpp-api/vapi/vapi_internal.h @@ -118,8 +118,18 @@ bool vapi_requests_full (vapi_ctx_t ctx); size_t vapi_get_request_count (vapi_ctx_t ctx); size_t vapi_get_max_request_count (vapi_ctx_t ctx); u32 vapi_gen_req_context (vapi_ctx_t ctx); -void vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump, - vapi_cb_t callback, void *callback_ctx); + +enum vapi_request_type +{ + VAPI_REQUEST_REG = 0, + VAPI_REQUEST_DUMP = 1, + VAPI_REQUEST_STREAM = 2, +}; + +void vapi_store_request (vapi_ctx_t ctx, u32 context, + vapi_msg_id_t response_id, + enum vapi_request_type type, vapi_cb_t callback, + void *callback_ctx); int vapi_get_payload_offset (vapi_msg_id_t id); void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *payload); void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *payload); diff --git a/src/vpp-api/vapi/vapi_json_parser.py b/src/vpp-api/vapi/vapi_json_parser.py index a323f15e7b6..c06cb8cf77b 100644 --- a/src/vpp-api/vapi/vapi_json_parser.py +++ b/src/vpp-api/vapi/vapi_json_parser.py @@ -158,6 +158,7 @@ class Message(object): self.header = None self.is_reply = json_parser.is_reply(self.name) self.is_event = json_parser.is_event(self.name) + self.is_stream = json_parser.is_stream(self.name) fields = [] for header in get_msg_header_defs( struct_type_class, field_class, json_parser, logger @@ -196,12 +197,18 @@ class Message(object): "array `%s' doesn't have reference to member " "containing the actual length" % (name, field[1]) ) - if field[0] == "string" and field[2] > 0: - field_type = json_parser.lookup_type_like_id("u8") + if field[0] == "string" and field[2] == 0: + field_type = json_parser.lookup_type_like_id("vl_api_string_t") + p = field_class(field_name=field[1], field_type=field_type) + else: + if field[0] == "string" and field[2] > 0: + field_type = json_parser.lookup_type_like_id("u8") - p = field_class( - field_name=field[1], field_type=field_type, array_len=field[2] - ) + p = field_class( + field_name=field[1], + field_type=field_type, + array_len=field[2], + ) elif l == 4: nelem_field = None for f in fields: @@ -254,13 +261,31 @@ class StructType(Type, Struct): p = field_class(field_name=field[1], field_type=field_type) elif len(field) == 3: if field[2] == 0: - raise ParseError( - "While parsing type `%s': array `%s' has " - "variable length" % (name, field[1]) + if name == "vl_api_string_t": + p = None + for f in fields: + if f.name == "length": + nelem_field = f + p = field_class( + field_name=field[1], + field_type=field_type, + array_len=field[2], + nelem_field=nelem_field, + ) + break + if p is None: + raise ParseError( + "While parsing type `%s': missing `length'" % name + ) + else: + raise ParseError( + "While parsing type `%s': array `%s' has " + "variable length" % (name, field[1]) + ) + else: + p = field_class( + field_name=field[1], field_type=field_type, array_len=field[2] ) - p = field_class( - field_name=field[1], field_type=field_type, array_len=field[2] - ) elif len(field) == 4: nelem_field = None for f in fields: @@ -324,6 +349,7 @@ class JsonParser(object): self.services = {} self.messages = {} self.enums = {} + self.enumflags = {} self.unions = {} self.aliases = {} self.types = { @@ -342,9 +368,16 @@ class JsonParser(object): ] } - self.types["string"] = simple_type_class("vl_api_string_t") + self.types["string"] = simple_type_class("u8") + self.types["vl_api_string_t"] = struct_type_class( + ["vl_api_string_t", ["u32", "length"], ["u8", "buf", 0]], + self, + field_class, + logger, + ) self.replies = set() self.events = set() + self.streams = set() self.simple_type_class = simple_type_class self.enum_class = enum_class self.union_class = union_class @@ -383,6 +416,8 @@ class JsonParser(object): if "events" in self.services[k]: for x in self.services[k]["events"]: self.events.add(x) + if "stream_msg" in self.services[k]: + self.streams.add(self.services[k]["stream_msg"]) for e in j["enums"]: name = e[0] value_pairs = e[1:-1] @@ -391,6 +426,14 @@ class JsonParser(object): self.enums[enum.name] = enum self.logger.debug("Parsed enum: %s" % enum) self.enums_by_json[path].append(enum) + for e in j["enumflags"]: + name = e[0] + value_pairs = e[1:-1] + enumtype = self.types[e[-1]["enumtype"]] + enum = self.enum_class(name, value_pairs, enumtype) + self.enums[enum.name] = enum + self.logger.debug("Parsed enumflag: %s" % enum) + self.enums_by_json[path].append(enum) exceptions = [] progress = 0 last_progress = 0 @@ -485,6 +528,8 @@ class JsonParser(object): return self.types[name] elif name in self.enums: return self.enums[name] + elif name in self.enumflags: + return self.enumflags[name] elif name in self.unions: return self.unions[name] elif name in self.aliases: @@ -493,6 +538,8 @@ class JsonParser(object): return self.types[mundane_name] elif mundane_name in self.enums: return self.enums[mundane_name] + elif mundane_name in self.enumflags: + return self.enumflags[mundane_name] elif mundane_name in self.unions: return self.unions[mundane_name] elif mundane_name in self.aliases: @@ -508,6 +555,20 @@ class JsonParser(object): def is_event(self, message): return message in self.events + def is_stream(self, message): + return message in self.streams + + def has_stream_msg(self, message): + return ( + message.name in self.services + and "stream_msg" in self.services[message.name] + ) + + def get_stream_msg(self, message): + if not self.has_stream_msg(message): + return None + return self.messages[self.services[message.name]["stream_msg"]] + def get_reply(self, message): return self.messages[self.services[message]["reply"]] @@ -519,13 +580,15 @@ class JsonParser(object): remove = [] for n, m in j.items(): try: - if not m.is_reply and not m.is_event: + if not m.is_reply and not m.is_event and not m.is_stream: try: m.reply = self.get_reply(n) + m.reply_is_stream = False + m.has_stream_msg = self.has_stream_msg(m) if "stream" in self.services[m.name]: m.reply_is_stream = self.services[m.name]["stream"] - else: - m.reply_is_stream = False + if m.has_stream_msg: + m.stream_msg = self.get_stream_msg(m) m.reply.request = m except: raise ParseError("Cannot find reply to message `%s'" % n) |