diff options
Diffstat (limited to 'telemetry')
-rw-r--r-- | telemetry/CMakeLists.txt | 21 | ||||
-rw-r--r-- | telemetry/vpp-collectd/CMakeLists.txt | 31 | ||||
-rw-r--r-- | telemetry/vpp-collectd/cmake/Modules/Packaging.cmake | 31 | ||||
-rw-r--r-- | telemetry/vpp-collectd/vpp-hicn/CMakeLists.txt | 55 | ||||
-rw-r--r-- | telemetry/vpp-collectd/vpp-hicn/vpp_hicn.c | 416 | ||||
-rw-r--r-- | telemetry/vpp-collectd/vpp/CMakeLists.txt | 42 | ||||
-rw-r--r-- | telemetry/vpp-collectd/vpp/vpp.c | 398 |
7 files changed, 994 insertions, 0 deletions
diff --git a/telemetry/CMakeLists.txt b/telemetry/CMakeLists.txt new file mode 100644 index 000000000..53fd04f01 --- /dev/null +++ b/telemetry/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +if ((CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) OR + (BUILD_HICNPLUGIN AND "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")) + add_subdirectory(vpp-collectd) +endif () + diff --git a/telemetry/vpp-collectd/CMakeLists.txt b/telemetry/vpp-collectd/CMakeLists.txt new file mode 100644 index 000000000..18926b1c5 --- /dev/null +++ b/telemetry/vpp-collectd/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +set (COLLECTD_PLUGINS hicn-collectd-plugins) +project(hicn-collectd-plugins) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/" "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules/") + +include(BuildMacros) + + +add_subdirectory(vpp) +add_subdirectory(vpp-hicn) + +include(Packaging) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + include(Packager) + make_packages() +endif() diff --git a/telemetry/vpp-collectd/cmake/Modules/Packaging.cmake b/telemetry/vpp-collectd/cmake/Modules/Packaging.cmake new file mode 100644 index 000000000..dc4629a26 --- /dev/null +++ b/telemetry/vpp-collectd/cmake/Modules/Packaging.cmake @@ -0,0 +1,31 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +###################### +# Packages section +###################### + +set(${COLLECTD_PLUGINS}_DESCRIPTION + "A high-performance Hybrid ICN forwarder as a plugin to VPP." + CACHE STRING "Description for deb/rpm package." +) + +set(${COLLECTD_PLUGINS}_DEB_DEPENDENCIES + "collectd, hicn-plugin-dev" + CACHE STRING "Dependencies for deb/rpm package." +) + +set(${COLLECTD_PLUGINS}_RPM_DEPENDENCIES + "collectd, hicn-plugin-dev" + CACHE STRING "Dependencies for deb/rpm package." +)
\ No newline at end of file diff --git a/telemetry/vpp-collectd/vpp-hicn/CMakeLists.txt b/telemetry/vpp-collectd/vpp-hicn/CMakeLists.txt new file mode 100644 index 000000000..ab048b730 --- /dev/null +++ b/telemetry/vpp-collectd/vpp-hicn/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +# Dependencies +find_package(Collectd REQUIRED) +find_package(Vpp REQUIRED) + +if(${CMAKE_SOURCE_DIR}/vpp-collectd STREQUAL ${PROJECT_SOURCE_DIR}) + message (STATUS "not compiling in the same folder") + find_package(HicnPlugin REQUIRED) + find_package(VapiSafe REQUIRED) +else() + message (STATUS "compiling in the same folder") + list(APPEND DEPENDENCIES + hicn_plugin + ) +endif() + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_hicn.c +) + +list (APPEND INCLUDE_DIRS + ${COLLECTD_INCLUDE_DIRS} + ${HICNPLUGIN_INCLUDE_DIRS} + ${SAFE_VAPI_INCLUDE_DIRS} + ${VPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}) + +list (APPEND LIBRARIES + ${VPP_LIBRARY_VAPICLIENT} + ${SAFE_VAPI_LIBRARIES}) + +build_library(vpp_hicn + SHARED + SOURCES ${SOURCE_FILES} + LINK_LIBRARIES ${LIBRARIES} + INCLUDE_DIRS ${INCLUDE_DIRS} + INSTALL_FULL_PATH_DIR ${CMAKE_INSTALL_PREFIX}/collectd + COMPONENT "${COLLECTD_PLUGINS}" + DEPENDS ${DEPENDENCIES} + EMPTY_PREFIX true + ) diff --git a/telemetry/vpp-collectd/vpp-hicn/vpp_hicn.c b/telemetry/vpp-collectd/vpp-hicn/vpp_hicn.c new file mode 100644 index 000000000..b90646073 --- /dev/null +++ b/telemetry/vpp-collectd/vpp-hicn/vpp_hicn.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !HAVE_CONFIG_H +#include <stdlib.h> +#include <string.h> + +#ifndef __USE_ISOC99 /* required for NAN */ +#define DISABLE_ISOC99 1 +#define __USE_ISOC99 1 +#endif /* !defined(__USE_ISOC99) */ + +#if DISABLE_ISOC99 +#undef DISABLE_ISOC99 +#undef __USE_ISOC99 +#endif /* DISABLE_ISOC99 */ +#endif /* ! HAVE_CONFIG */ + +#include <config.h> +#include <collectd.h> +#include <common.h> +#include <plugin.h> + +#define counter_t vpp_counter_t +#include <vapi/hicn.api.vapi.h> +#include <vapi/vapi_safe.h> +#undef counter_t + +DEFINE_VAPI_MSG_IDS_HICN_API_JSON +vapi_ctx_t vapi_ctx; + +/************** DATA SOURCES ******************************/ +static data_source_t packets_dsrc[1] = { + {"packets", DS_TYPE_GAUGE, 0, NAN}, +}; + +static data_source_t interests_dsrc[1] = { + {"interests", DS_TYPE_GAUGE, 0, NAN}, +}; + +static data_source_t data_dsrc[1] = { + {"data", DS_TYPE_GAUGE, 0, NAN}, +}; + +static data_source_t combined_dsrc[2] = { + {"packets", DS_TYPE_DERIVE, 0, NAN}, + {"bytes", DS_TYPE_DERIVE, 0, NAN}, +}; + +/************** DATA SETS NODE ****************************/ +static data_set_t pkts_processed_ds = { + "pkts_processed", + STATIC_ARRAY_SIZE(packets_dsrc), + packets_dsrc, +}; + +static data_set_t pkts_interest_count_ds = { + "pkts_interest_count", + STATIC_ARRAY_SIZE(packets_dsrc), + packets_dsrc, +}; + +static data_set_t pkts_data_count_ds = { + "pkts_data_count", + STATIC_ARRAY_SIZE(packets_dsrc), + packets_dsrc, +}; + +static data_set_t pkts_from_cache_count_ds = { + "pkts_from_cache_count", + STATIC_ARRAY_SIZE(packets_dsrc), + packets_dsrc, +}; + +static data_set_t pkts_no_pit_count_ds = { + "pkts_no_pit_count", + STATIC_ARRAY_SIZE(packets_dsrc), + packets_dsrc, +}; + +static data_set_t pit_expired_count_ds = { + "pit_expired_count", + STATIC_ARRAY_SIZE(interests_dsrc), + interests_dsrc, +}; + +static data_set_t cs_expired_count_ds = { + "cs_expired_count", + STATIC_ARRAY_SIZE(data_dsrc), + data_dsrc, +}; + +static data_set_t cs_lru_count_ds = { + "cs_lru_count", + STATIC_ARRAY_SIZE(data_dsrc), + data_dsrc, +}; + +static data_set_t pkts_drop_no_buf_ds = { + "pkts_drop_no_buf", + STATIC_ARRAY_SIZE(packets_dsrc), + packets_dsrc, +}; + +static data_set_t interests_aggregated_ds = { + "interests_aggregated", + STATIC_ARRAY_SIZE(interests_dsrc), + interests_dsrc, +}; + +static data_set_t interests_retx_ds = { + "interests_retx", + STATIC_ARRAY_SIZE(interests_dsrc), + interests_dsrc, +}; + +static data_set_t interests_hash_collision_ds = { + "interests_hash_collision", + STATIC_ARRAY_SIZE(interests_dsrc), + interests_dsrc, +}; + +static data_set_t pit_entries_count_ds = { + "pit_entries_count", + STATIC_ARRAY_SIZE(interests_dsrc), + interests_dsrc, +}; + +static data_set_t cs_entries_count_ds = { + "cs_entries_count", + STATIC_ARRAY_SIZE(data_dsrc), + data_dsrc, +}; + +static data_set_t cs_entries_ntw_count_ds = { + "cs_entries_ntw_count", + STATIC_ARRAY_SIZE(data_dsrc), + data_dsrc, +}; + +/************** DATA SETS FACE ****************************/ +static data_set_t irx_ds = { + "irx", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t itx_ds = { + "itx", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t drx_ds = { + "drx", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t dtx_ds = { + "dtx", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +/**********************************************************/ +/********** UTILITY FUNCTIONS *****************************/ +/**********************************************************/ + +/* + * Utility function used by the read callback to populate a + * value_list_t and pass it to plugin_dispatch_values. + */ +static int submit(const char *plugin_instance, const char *type, + const char *type_instance, value_t *values, size_t values_len, + cdtime_t *timestamp) { + value_list_t vl = VALUE_LIST_INIT; + vl.values = values; + vl.values_len = values_len; + + if (timestamp != NULL) { + vl.time = *timestamp; + } + + sstrncpy(vl.plugin, "vpp_hicn", sizeof(vl.plugin)); + sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + sstrncpy(vl.type, type, sizeof(vl.type)); + + if (type_instance != NULL) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + + return plugin_dispatch_values(&vl); +} + +/**********************************************************/ +/********** CALLBACK FUNCTIONS ****************************/ +/**********************************************************/ + +/* + * Callback called by the hICN plugin API when node stats are ready. + */ +static vapi_error_e +parse_node_stats(vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, + bool is_last, + vapi_payload_hicn_api_node_stats_get_reply *reply) { + if (reply == NULL || rv != VAPI_OK) + return rv; + + if (reply->retval != VAPI_OK) + return reply->retval; + + char *node_name = "node"; + value_t values[1]; + cdtime_t timestamp = cdtime(); + + values[0] = (value_t){.gauge = reply->pkts_processed}; + submit(node_name, pkts_processed_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->pkts_interest_count}; + submit(node_name, pkts_interest_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->pkts_data_count}; + submit(node_name, pkts_data_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->pkts_from_cache_count}; + submit(node_name, pkts_from_cache_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->pkts_no_pit_count}; + submit(node_name, pkts_no_pit_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->pit_expired_count}; + submit(node_name, pit_expired_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->cs_expired_count}; + submit(node_name, cs_expired_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->cs_lru_count}; + submit(node_name, cs_lru_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->pkts_drop_no_buf}; + submit(node_name, pkts_drop_no_buf_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->interests_aggregated}; + submit(node_name, interests_aggregated_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->interests_retx}; + submit(node_name, interests_retx_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->interests_hash_collision}; + submit(node_name, interests_hash_collision_ds.type, NULL, values, 1, + ×tamp); + values[0] = (value_t){.gauge = reply->pit_entries_count}; + submit(node_name, pit_entries_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->cs_entries_count}; + submit(node_name, cs_entries_count_ds.type, NULL, values, 1, ×tamp); + values[0] = (value_t){.gauge = reply->cs_entries_ntw_count}; + submit(node_name, cs_entries_ntw_count_ds.type, NULL, values, 1, ×tamp); + + return VAPI_OK; +} + +/* + * Callback called by the hICN plugin API when face stats are ready. + */ +static vapi_error_e +parse_face_stats(vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, + bool is_last, + vapi_payload_hicn_api_face_stats_details *reply) { + if (reply == NULL || rv != VAPI_OK) + return rv; + + if (reply->retval != VAPI_OK) + return reply->retval; + + char face_name[10]; + snprintf(face_name, 10, "face%u", reply->faceid); + value_t values[2]; + cdtime_t timestamp = cdtime(); + + values[0] = (value_t){.derive = reply->irx_packets}; + values[1] = (value_t){.derive = reply->irx_bytes}; + submit(face_name, irx_ds.type, NULL, values, 2, ×tamp); + values[0] = (value_t){.derive = reply->itx_packets}; + values[1] = (value_t){.derive = reply->itx_bytes}; + submit(face_name, itx_ds.type, NULL, values, 2, ×tamp); + values[0] = (value_t){.derive = reply->drx_packets}; + values[1] = (value_t){.derive = reply->drx_bytes}; + submit(face_name, drx_ds.type, NULL, values, 2, ×tamp); + values[0] = (value_t){.derive = reply->dtx_packets}; + values[1] = (value_t){.derive = reply->dtx_bytes}; + submit(face_name, dtx_ds.type, NULL, values, 2, ×tamp); + + return VAPI_OK; +} + +/* + * This function is called once upon startup to initialize the plugin. + */ +static int my_init(void) { + int ret = vapi_connect_safe(&vapi_ctx, 0); + + if (ret) + plugin_log(LOG_ERR, "vpp_hicn plugin: vapi_connect_safe failed"); + + return ret; +} + +/* + * This function is called in regular intervalls to collect the data. + */ +static int my_read(void) { + int err = VAPI_OK; + + vapi_lock(); + + // NODE + vapi_msg_hicn_api_node_stats_get *hicn_node_stats_msg; + hicn_node_stats_msg = vapi_alloc_hicn_api_node_stats_get(vapi_ctx); + + if (!hicn_node_stats_msg) { + plugin_log(LOG_ERR, + "vpp_hicn plugin: could not create hicn_node_stats message"); + err = VAPI_ENOMEM; + goto END; + } + + err = vapi_hicn_api_node_stats_get(vapi_ctx, hicn_node_stats_msg, + parse_node_stats, NULL); + + if (err) { + plugin_log(LOG_ERR, + "vpp_hicn plugin: query of node stats failed with error %d", + err); + goto END; + } + + // FACES + vapi_msg_hicn_api_face_stats_dump *hicn_face_stats_msg; + hicn_face_stats_msg = vapi_alloc_hicn_api_face_stats_dump(vapi_ctx); + + if (!hicn_face_stats_msg) { + plugin_log(LOG_ERR, + "vpp_hicn plugin: could not create hicn_face_stats message"); + err = VAPI_ENOMEM; + goto END; + } + + err = vapi_hicn_api_face_stats_dump(vapi_ctx, hicn_face_stats_msg, + parse_face_stats, NULL); + + if (err) { + plugin_log(LOG_ERR, + "vpp_hicn plugin: query of face stats failed with error %d", + err); + goto END; + } + +END: + vapi_unlock(); + + return err; +} + +/* + * This function is called when plugin_log () has been used. + */ +static void my_log(int severity, const char *msg, user_data_t *ud) { + printf("[LOG %i] %s\n", severity, msg); + return; +} + +/* + * This function is called before shutting down collectd. + */ +static int my_shutdown(void) { + plugin_log(LOG_INFO, "vpp_hicn plugin: shutting down"); + int ret = vapi_disconnect_safe(); + plugin_log(LOG_INFO, "vpp_hicn plugin: disconnect vapi %s", + ret == 0 ? "ok" : "error"); + return ret; +} + +/* + * This function is called after loading the plugin to register it with + * collectd. + */ +void module_register(void) { + // data sets face + plugin_register_data_set(&irx_ds); + plugin_register_data_set(&itx_ds); + plugin_register_data_set(&drx_ds); + plugin_register_data_set(&dtx_ds); + // data sets node + plugin_register_data_set(&pkts_processed_ds); + plugin_register_data_set(&pkts_interest_count_ds); + plugin_register_data_set(&pkts_data_count_ds); + plugin_register_data_set(&pkts_from_cache_count_ds); + plugin_register_data_set(&pkts_no_pit_count_ds); + plugin_register_data_set(&pit_expired_count_ds); + plugin_register_data_set(&cs_expired_count_ds); + plugin_register_data_set(&cs_lru_count_ds); + plugin_register_data_set(&pkts_drop_no_buf_ds); + plugin_register_data_set(&interests_aggregated_ds); + plugin_register_data_set(&interests_retx_ds); + plugin_register_data_set(&interests_hash_collision_ds); + plugin_register_data_set(&pit_entries_count_ds); + plugin_register_data_set(&cs_entries_count_ds); + plugin_register_data_set(&cs_entries_ntw_count_ds); + // callbacks + plugin_register_log("vpp_hicn", my_log, /* user data */ NULL); + plugin_register_init("vpp_hicn", my_init); + plugin_register_read("vpp_hicn", my_read); + plugin_register_shutdown("vpp_hicn", my_shutdown); + return; +} diff --git a/telemetry/vpp-collectd/vpp/CMakeLists.txt b/telemetry/vpp-collectd/vpp/CMakeLists.txt new file mode 100644 index 000000000..71fb7fc3d --- /dev/null +++ b/telemetry/vpp-collectd/vpp/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +# Dependencies + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vpp.c +) + +find_package(Vpp REQUIRED) +find_package(Collectd REQUIRED) + +list (APPEND INCLUDE_DIRS + ${COLLECTD_INCLUDE_DIRS} + ${VPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}) + +list (APPEND LIBRARIES + ${VPP_LIBRARY_VPPAPICLIENT} + ${VPP_LIBRARY_INFRA}) + +build_library(vpp + SHARED + SOURCES ${SOURCE_FILES} + LINK_LIBRARIES ${LIBRARIES} + INCLUDE_DIRS ${INCLUDE_DIRS} + INSTALL_FULL_PATH_DIR ${CMAKE_INSTALL_PREFIX}/collectd + COMPONENT ${COLLECTD_PLUGINS} + EMPTY_PREFIX true + ) diff --git a/telemetry/vpp-collectd/vpp/vpp.c b/telemetry/vpp-collectd/vpp/vpp.c new file mode 100644 index 000000000..72c052212 --- /dev/null +++ b/telemetry/vpp-collectd/vpp/vpp.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !HAVE_CONFIG_H +#include <stdlib.h> +#include <string.h> + +#ifndef __USE_ISOC99 /* required for NAN */ +#define DISABLE_ISOC99 1 +#define __USE_ISOC99 1 +#endif /* !defined(__USE_ISOC99) */ + +#if DISABLE_ISOC99 +#undef DISABLE_ISOC99 +#undef __USE_ISOC99 +#endif /* DISABLE_ISOC99 */ +#endif /* ! HAVE_CONFIG */ + +#include <config.h> +#include <collectd.h> +#include <common.h> +#include <plugin.h> + +#define counter_t vpp_counter_t +#include <vpp-api/client/stat_client.h> +#include <vppinfra/vec.h> +#undef counter_t + +/************** DATA SOURCES ******************************/ +static data_source_t combined_dsrc[2] = { + {"packets", DS_TYPE_DERIVE, 0, NAN}, + {"bytes", DS_TYPE_DERIVE, 0, NAN}, +}; + +static data_source_t simple_dsrc[1] = { + {"packets", DS_TYPE_DERIVE, 0, NAN}, +}; + +/************** DATA SETS *********************************/ +static data_set_t if_drops_ds = { + "if_drops", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_punt_ds = { + "if_punt", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_ip4_ds = { + "if_ip4", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_ip6_ds = { + "if_ip6", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_rx_no_buf_ds = { + "if_rx_no_buf", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_rx_miss_ds = { + "if_rx_miss", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_rx_error_ds = { + "if_rx_error", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_tx_error_ds = { + "if_tx_error", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_mpls_ds = { + "if_mpls", + STATIC_ARRAY_SIZE(simple_dsrc), + simple_dsrc, +}; + +static data_set_t if_rx_ds = { + "if_rx", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_rx_unicast_ds = { + "if_rx_unicast", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_rx_multicast_ds = { + "if_rx_multicast", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_rx_broadcast_ds = { + "if_rx_broadcast", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_tx_ds = { + "if_tx", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_tx_unicast_ds = { + "if_tx_unicast", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_tx_multicast_ds = { + "if_tx_multicast", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +static data_set_t if_tx_broadcast_ds = { + "if_tx_broadcast", + STATIC_ARRAY_SIZE(combined_dsrc), + combined_dsrc, +}; + +/**********************************************************/ +/********** UTILITY FUNCTIONS *****************************/ +/**********************************************************/ + +/* + * Utility function used by the read callback to populate a + * value_list_t and pass it to plugin_dispatch_values. + */ +static int submit(const char *plugin_instance, const char *type, + const char *type_instance, value_t *values, size_t values_len, + cdtime_t *timestamp) { + value_list_t vl = VALUE_LIST_INIT; + vl.values = values; + vl.values_len = values_len; + + if (timestamp != NULL) { + vl.time = *timestamp; + } + + sstrncpy(vl.plugin, "vpp", sizeof(vl.plugin)); + sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + sstrncpy(vl.type, type, sizeof(vl.type)); + + if (type_instance != NULL) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + + return plugin_dispatch_values(&vl); +} + +/* + * Utility function to fetch the data set corresponding to the stat name. + */ +static int get_data_set(const char *stat_name, data_set_t *data_set_ptr) { + if (data_set_ptr == NULL) { + return 1; + } + + if (strcmp(stat_name, "/if/drops") == 0) { + *data_set_ptr = if_drops_ds; + } else if (strcmp(stat_name, "/if/punt") == 0) { + *data_set_ptr = if_punt_ds; + } else if (strcmp(stat_name, "/if/ip4") == 0) { + *data_set_ptr = if_ip4_ds; + } else if (strcmp(stat_name, "/if/ip6") == 0) { + *data_set_ptr = if_ip6_ds; + } else if (strcmp(stat_name, "/if/rx-no-buf") == 0) { + *data_set_ptr = if_rx_no_buf_ds; + } else if (strcmp(stat_name, "/if/rx-miss") == 0) { + *data_set_ptr = if_rx_miss_ds; + } else if (strcmp(stat_name, "/if/rx-error") == 0) { + *data_set_ptr = if_rx_error_ds; + } else if (strcmp(stat_name, "/if/tx-error") == 0) { + *data_set_ptr = if_tx_error_ds; + } else if (strcmp(stat_name, "/if/mpls") == 0) { + *data_set_ptr = if_mpls_ds; + } else if (strcmp(stat_name, "/if/rx") == 0) { + *data_set_ptr = if_rx_ds; + } else if (strcmp(stat_name, "/if/rx-unicast") == 0) { + *data_set_ptr = if_rx_unicast_ds; + } else if (strcmp(stat_name, "/if/rx-multicast") == 0) { + *data_set_ptr = if_rx_multicast_ds; + } else if (strcmp(stat_name, "/if/rx-broadcast") == 0) { + *data_set_ptr = if_rx_broadcast_ds; + } else if (strcmp(stat_name, "/if/tx") == 0) { + *data_set_ptr = if_tx_ds; + } else if (strcmp(stat_name, "/if/tx-unicast") == 0) { + *data_set_ptr = if_tx_unicast_ds; + } else if (strcmp(stat_name, "/if/tx-multicast") == 0) { + *data_set_ptr = if_tx_multicast_ds; + } else if (strcmp(stat_name, "/if/tx-broadcast") == 0) { + *data_set_ptr = if_tx_broadcast_ds; + } else { + return 1; + } + + return 0; +} + +/**********************************************************/ +/********** CALLBACK FUNCTIONS ****************************/ +/**********************************************************/ + +/* + * This function is called once upon startup to initialize the plugin. + */ +static int my_init(void) { + u8 *stat_segment_name = (u8 *)STAT_SEGMENT_SOCKET_FILE; + int ret = stat_segment_connect((char *)stat_segment_name); + + if (ret) + plugin_log(LOG_ERR, "vpp plugin: connecting to segment failed"); + + return ret; +} + +/* + * This function is called in regular intervalls to collect the data. + */ +static int my_read(void) { + uint8_t **patterns = {0}; + char **interfaces = {0}; + + vec_add1(patterns, (uint8_t *)"^/if"); + vec_add1(patterns, (uint8_t *)"ip4-input"); + + uint32_t *dir = stat_segment_ls(patterns); + stat_segment_data_t *res = stat_segment_dump(dir); + plugin_log(LOG_INFO, "vpp plugin: performed ls and dump"); + + /* Read all available interfaces */ + for (int k = 0; k < vec_len(res); k++) { + if (res[k].type == STAT_DIR_TYPE_NAME_VECTOR) { + for (int i = 0; i < vec_len(res[k].name_vector); i++) { + if (res[k].name_vector[i]) { + vec_add1(interfaces, (char *)res[k].name_vector[i]); + } + } + break; + } + } + + cdtime_t timestamp = cdtime(); + data_set_t data_set; + int err = 0; + + /* Collect results for each interface and submit them */ + for (int i = 0; i < vec_len(res); i++) { + switch (res[i].type) { + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + for (int k = 0; k < vec_len(res[i].simple_counter_vec); k++) { + for (int j = 0; j < vec_len(res[i].simple_counter_vec[k]); j++) { + value_t values[1] = { + (value_t){.derive = res[i].simple_counter_vec[k][j]}}; + + if (get_data_set(res[i].name, &data_set)) { + plugin_log(LOG_INFO, "vpp plugin: ignored stat name %s", + res[i].name); + continue; + } + + err = + submit(interfaces[j], data_set.type, NULL, values, 1, ×tamp); + + if (err) + goto END; + } + } + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + for (int k = 0; k < vec_len(res[i].combined_counter_vec); k++) { + for (int j = 0; j < vec_len(res[i].combined_counter_vec[k]); j++) { + value_t values[2] = { + (value_t){.derive = res[i].combined_counter_vec[k][j].packets}, + (value_t){.derive = res[i].combined_counter_vec[k][j].bytes}, + }; + + if (get_data_set(res[i].name, &data_set)) { + plugin_log(LOG_INFO, "vpp plugin: ignored stat name %s", + res[i].name); + continue; + } + + err = + submit(interfaces[j], data_set.type, NULL, values, 2, ×tamp); + + if (err) + goto END; + } + } + break; + + case STAT_DIR_TYPE_SCALAR_INDEX: + plugin_log(LOG_INFO, "vpp plugin: %.2f %s", res[i].scalar_value, + res[i].name); + break; + + case STAT_DIR_TYPE_NAME_VECTOR: + break; + + case STAT_DIR_TYPE_ERROR_INDEX: + break; + + default: + plugin_log(LOG_WARNING, "vpp plugin: unknown stat type %d", res[i].type); + break; + } + } + +END: + if (err) + plugin_log(LOG_ERR, + "vpp plugin: dispatching of results failed with error code %d.", + err); + + stat_segment_data_free(res); + + return err; +} + +/* + * This function is called when plugin_log () has been used. + */ +static void my_log(int severity, const char *msg, user_data_t *ud) { + printf("[LOG %i] %s\n", severity, msg); + return; +} + +/* + * This function is called before shutting down collectd. + */ +static int my_shutdown(void) { + plugin_log(LOG_INFO, "vpp plugin: shutting down"); + stat_segment_disconnect(); + return 0; +} + +/* + * This function is called after loading the plugin to register it with + * collectd. + */ +void module_register(void) { + plugin_register_data_set(&if_drops_ds); + plugin_register_data_set(&if_punt_ds); + plugin_register_data_set(&if_ip4_ds); + plugin_register_data_set(&if_ip6_ds); + plugin_register_data_set(&if_rx_no_buf_ds); + plugin_register_data_set(&if_rx_miss_ds); + plugin_register_data_set(&if_rx_error_ds); + plugin_register_data_set(&if_tx_error_ds); + plugin_register_data_set(&if_mpls_ds); + plugin_register_data_set(&if_rx_ds); + plugin_register_data_set(&if_rx_unicast_ds); + plugin_register_data_set(&if_rx_multicast_ds); + plugin_register_data_set(&if_rx_broadcast_ds); + plugin_register_data_set(&if_tx_ds); + plugin_register_data_set(&if_tx_unicast_ds); + plugin_register_data_set(&if_tx_multicast_ds); + plugin_register_data_set(&if_tx_broadcast_ds); + plugin_register_log("vpp", my_log, /* user data */ NULL); + plugin_register_init("vpp", my_init); + plugin_register_read("vpp", my_read); + plugin_register_shutdown("vpp", my_shutdown); + return; +} |