numam-dpdk/lib/librte_telemetry/rte_telemetry.c
Ciara Power c5b7197f66 telemetry: move some functions to metrics library
This commit moves some of the telemetry library code to a new file in
the metrics library. No modifications are made to the moved code,
except what is needed to allow it to compile and run. The additional
code in metrics is built only when the Jansson library is  present.
Telemetry functions as normal, using the functions from the
metrics_telemetry file. This move will enable code be reused by the new
version of telemetry in a later commit, to support backward
compatibility with the existing telemetry usage.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Reviewed-by: Keith Wiles <keith.wiles@intel.com>
2020-05-10 23:46:18 +02:00

1386 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2018 Intel Corporation
*/
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <jansson.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
#include <rte_option.h>
#include <rte_string_fns.h>
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#include "rte_telemetry_parser.h"
#include "rte_telemetry_socket_tests.h"
#define BUF_SIZE 1024
#define ACTION_POST 1
#define SLEEP_TIME 10
#define SELFTEST_VALID_CLIENT "/var/run/dpdk/valid_client"
#define SELFTEST_INVALID_CLIENT "/var/run/dpdk/invalid_client"
#define SOCKET_TEST_CLIENT_PATH "/var/run/dpdk/client"
static telemetry_impl *static_telemetry;
struct telemetry_message_test {
const char *test_name;
int (*test_func_ptr)(struct telemetry_impl *telemetry, int fd);
};
struct json_data {
char *status_code;
const char *data;
int port;
char *stat_name;
int stat_value;
};
static void
rte_telemetry_get_runtime_dir(char *socket_path, size_t size)
{
snprintf(socket_path, size, "%s/telemetry", rte_eal_get_runtime_dir());
}
static int32_t
rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
const char *json_string)
{
int ret;
if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Could not initialise TELEMETRY_API");
return -1;
}
if (telemetry->request_client == NULL) {
TELEMETRY_LOG_ERR("No client has been chosen to write to");
return -1;
}
if (json_string == NULL) {
TELEMETRY_LOG_ERR("Invalid JSON string!");
return -1;
}
ret = send(telemetry->request_client->fd,
json_string, strlen(json_string), 0);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to write to socket for client: %s",
telemetry->request_client->file_path);
return -1;
}
return 0;
}
int32_t
rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
int error_type)
{
int ret;
const char *status_code, *json_buffer;
json_t *root;
if (error_type == -EPERM)
status_code = "Status Error: Unknown";
else if (error_type == -EINVAL)
status_code = "Status Error: Invalid Argument 404";
else if (error_type == -ENOMEM)
status_code = "Status Error: Memory Allocation Error";
else {
TELEMETRY_LOG_ERR("Invalid error type");
return -EINVAL;
}
root = json_object();
if (root == NULL) {
TELEMETRY_LOG_ERR("Could not create root JSON object");
return -EPERM;
}
ret = json_object_set_new(root, "status_code", json_string(status_code));
if (ret < 0) {
TELEMETRY_LOG_ERR("Status code field cannot be set");
json_decref(root);
return -EPERM;
}
ret = json_object_set_new(root, "data", json_null());
if (ret < 0) {
TELEMETRY_LOG_ERR("Data field cannot be set");
json_decref(root);
return -EPERM;
}
json_buffer = json_dumps(root, 0);
json_decref(root);
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not write to socket");
return -EPERM;
}
return 0;
}
int32_t
rte_telemetry_send_global_stats_values(struct telemetry_encode_param *ep,
struct telemetry_impl *telemetry)
{
int ret;
char *json_buffer = NULL;
if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Invalid telemetry argument");
return -1;
}
if (ep->gp.num_metric_ids < 0) {
TELEMETRY_LOG_ERR("Invalid num_metric_ids, must be positive");
goto einval_fail;
}
ret = rte_metrics_tel_encode_json_format(ep, &json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("JSON encode function failed");
ret = rte_telemetry_send_error_response(telemetry, ret);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not send error");
return -1;
}
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not write to socket");
return -1;
}
return 0;
einval_fail:
ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not send error");
return -1;
}
int32_t
rte_telemetry_send_ports_stats_values(struct telemetry_encode_param *ep,
struct telemetry_impl *telemetry)
{
int ret;
char *json_buffer = NULL;
if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Invalid telemetry argument");
return -1;
}
if (ep == NULL) {
TELEMETRY_LOG_ERR("Invalid encode param argument");
goto einval_fail;
}
if (ep->pp.num_metric_ids < 0) {
TELEMETRY_LOG_ERR("Invalid num_metric_ids, must be positive");
goto einval_fail;
}
if (ep->pp.num_port_ids < 0) {
TELEMETRY_LOG_ERR("Invalid num_port_ids, must be positive");
goto einval_fail;
}
ret = rte_metrics_tel_get_ports_stats_json(ep, telemetry->reg_index,
&json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Function for get_ports_stats_json"
" failed");
ret = rte_telemetry_send_error_response(telemetry, ret);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not send error");
return -1;
}
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not write to socket");
return -1;
}
return 0;
einval_fail:
ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not send error");
return -1;
}
static int32_t
rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
int ret;
int selftest = 0;
ret = rte_metrics_tel_reg_all_ethdev(
&telemetry->metrics_register_done,
telemetry->reg_index);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to register ethdev metrics");
ret = rte_telemetry_send_error_response(telemetry, ret);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not send error");
return -1;
}
if (selftest) {
ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index[0],
telemetry->server_fd);
if (ret < 0)
return -1;
ret = rte_telemetry_parser_test(telemetry);
if (ret < 0) {
TELEMETRY_LOG_ERR("Parser Tests Failed");
return -1;
}
TELEMETRY_LOG_INFO("Success - All Parser Tests Passed");
}
return 0;
}
static int32_t
rte_telemetry_read_client(struct telemetry_impl *telemetry)
{
char buf[BUF_SIZE];
int ret, buffer_read;
buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
if (buffer_read == -1) {
TELEMETRY_LOG_ERR("Read error");
return -1;
} else if (buffer_read == 0) {
goto close_socket;
} else {
buf[buffer_read] = '\0';
ret = rte_telemetry_parse_client_message(telemetry, buf);
if (ret < 0)
TELEMETRY_LOG_WARN("Parse message failed");
goto close_socket;
}
close_socket:
if (close(telemetry->accept_fd) < 0) {
TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
free(telemetry);
return -EPERM;
}
telemetry->accept_fd = 0;
return 0;
}
static int32_t
rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
{
int ret;
if (telemetry->accept_fd <= 0) {
ret = listen(telemetry->server_fd, 1);
if (ret < 0) {
TELEMETRY_LOG_ERR("Listening error with server fd");
return -1;
}
telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
if (telemetry->accept_fd >= 0 &&
telemetry->metrics_register_done == 0) {
ret = rte_telemetry_initial_accept(telemetry);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to run initial configurations/tests");
return -1;
}
}
} else {
ret = rte_telemetry_read_client(telemetry);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to read socket buffer");
return -1;
}
}
return 0;
}
static int32_t
rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
{
int ret;
telemetry_client *client;
char client_buf[BUF_SIZE];
int bytes;
TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
bytes = read(client->fd, client_buf, BUF_SIZE-1);
if (bytes > 0) {
client_buf[bytes] = '\0';
telemetry->request_client = client;
ret = rte_telemetry_parse(telemetry, client_buf);
if (ret < 0) {
TELEMETRY_LOG_WARN("Parse socket input failed: %i",
ret);
return -1;
}
}
}
return 0;
}
static int32_t
rte_telemetry_run(void *userdata)
{
int ret;
struct telemetry_impl *telemetry = userdata;
if (telemetry == NULL) {
TELEMETRY_LOG_WARN("TELEMETRY could not be initialised");
return -1;
}
ret = rte_telemetry_accept_new_client(telemetry);
if (ret < 0) {
TELEMETRY_LOG_ERR("Accept and read new client failed");
return -1;
}
ret = rte_telemetry_read_client_sockets(telemetry);
if (ret < 0) {
TELEMETRY_LOG_ERR("Client socket read failed");
return -1;
}
return 0;
}
static void
*rte_telemetry_run_thread_func(void *userdata)
{
int ret;
struct telemetry_impl *telemetry = userdata;
if (telemetry == NULL) {
TELEMETRY_LOG_ERR("%s passed a NULL instance", __func__);
pthread_exit(0);
}
while (telemetry->thread_status) {
rte_telemetry_run(telemetry);
ret = usleep(SLEEP_TIME);
if (ret < 0)
TELEMETRY_LOG_ERR("Calling thread could not be put to sleep");
}
pthread_exit(0);
}
static int32_t
rte_telemetry_set_socket_nonblock(int fd)
{
int flags;
if (fd < 0) {
TELEMETRY_LOG_ERR("Invalid fd provided");
return -1;
}
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
static int32_t
rte_telemetry_create_socket(struct telemetry_impl *telemetry)
{
int ret;
struct sockaddr_un addr;
char socket_path[BUF_SIZE];
if (telemetry == NULL)
return -1;
telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (telemetry->server_fd == -1) {
TELEMETRY_LOG_ERR("Failed to open socket");
return -1;
}
ret = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
goto close_socket;
}
addr.sun_family = AF_UNIX;
rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
unlink(socket_path);
if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
sizeof(addr)) < 0) {
TELEMETRY_LOG_ERR("Socket binding error");
goto close_socket;
}
return 0;
close_socket:
if (close(telemetry->server_fd) < 0) {
TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
return -EPERM;
}
return -1;
}
int32_t
rte_telemetry_init(void)
{
int ret;
pthread_attr_t attr;
const char *telemetry_ctrl_thread = "telemetry";
if (static_telemetry) {
TELEMETRY_LOG_WARN("TELEMETRY structure already initialised");
return -EALREADY;
}
static_telemetry = calloc(1, sizeof(struct telemetry_impl));
if (static_telemetry == NULL) {
TELEMETRY_LOG_ERR("Memory could not be allocated");
return -ENOMEM;
}
static_telemetry->socket_id = rte_socket_id();
rte_metrics_init(static_telemetry->socket_id);
ret = pthread_attr_init(&attr);
if (ret != 0) {
TELEMETRY_LOG_ERR("Pthread attribute init failed");
return -EPERM;
}
ret = rte_telemetry_create_socket(static_telemetry);
if (ret < 0) {
ret = rte_telemetry_cleanup();
if (ret < 0)
TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
return -EPERM;
}
TAILQ_INIT(&static_telemetry->client_list_head);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
(void *)static_telemetry);
static_telemetry->thread_status = 1;
if (ret < 0) {
ret = rte_telemetry_cleanup();
if (ret < 0)
TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
return -EPERM;
}
return 0;
}
static int32_t
rte_telemetry_client_cleanup(struct telemetry_client *client)
{
int ret;
ret = close(client->fd);
free(client->file_path);
free(client);
if (ret < 0) {
TELEMETRY_LOG_ERR("Close client socket failed");
return -EPERM;
}
return 0;
}
int32_t
rte_telemetry_cleanup(void)
{
int ret;
struct telemetry_impl *telemetry = static_telemetry;
telemetry_client *client, *temp_client;
TAILQ_FOREACH_SAFE(client, &telemetry->client_list_head, client_list,
temp_client) {
TAILQ_REMOVE(&telemetry->client_list_head, client, client_list);
ret = rte_telemetry_client_cleanup(client);
if (ret < 0) {
TELEMETRY_LOG_ERR("Client cleanup failed");
return -EPERM;
}
}
ret = close(telemetry->server_fd);
if (ret < 0) {
TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
free(telemetry);
return -EPERM;
}
telemetry->thread_status = 0;
pthread_join(telemetry->thread_id, NULL);
free(telemetry);
static_telemetry = NULL;
return 0;
}
int32_t
rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
const char *client_path)
{
int ret;
telemetry_client *client, *temp_client;
if (telemetry == NULL) {
TELEMETRY_LOG_WARN("TELEMETRY is not initialised");
return -ENODEV;
}
if (client_path == NULL) {
TELEMETRY_LOG_ERR("Invalid client path");
goto einval_fail;
}
if (TAILQ_EMPTY(&telemetry->client_list_head)) {
TELEMETRY_LOG_ERR("There are no clients currently registered");
return -EPERM;
}
TAILQ_FOREACH_SAFE(client, &telemetry->client_list_head, client_list,
temp_client) {
if (strcmp(client_path, client->file_path) == 0) {
TAILQ_REMOVE(&telemetry->client_list_head, client,
client_list);
ret = rte_telemetry_client_cleanup(client);
if (ret < 0) {
TELEMETRY_LOG_ERR("Client cleanup failed");
return -EPERM;
}
return 0;
}
}
TELEMETRY_LOG_WARN("Couldn't find client, possibly not registered yet.");
return -1;
einval_fail:
ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not send error");
return -EINVAL;
}
int32_t
rte_telemetry_register_client(struct telemetry_impl *telemetry,
const char *client_path)
{
int ret, fd;
struct sockaddr_un addrs;
if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
return -ENODEV;
}
if (client_path == NULL) {
TELEMETRY_LOG_ERR("Invalid client path");
return -EINVAL;
}
telemetry_client *client;
TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
if (strcmp(client_path, client->file_path) == 0) {
TELEMETRY_LOG_WARN("'%s' already registered",
client_path);
return -EINVAL;
}
}
fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (fd == -1) {
TELEMETRY_LOG_ERR("Client socket error");
return -EACCES;
}
ret = rte_telemetry_set_socket_nonblock(fd);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
return -EPERM;
}
addrs.sun_family = AF_UNIX;
strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
telemetry_client *new_client = malloc(sizeof(telemetry_client));
new_client->file_path = strdup(client_path);
new_client->fd = fd;
if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
client_path);
ret = rte_telemetry_client_cleanup(new_client);
if (ret < 0) {
TELEMETRY_LOG_ERR("Client cleanup failed");
return -EPERM;
}
return -EINVAL;
}
TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
return 0;
}
int32_t
rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
{
int ret, action_int;
json_error_t error;
json_t *root = json_loads(buf, 0, &error);
if (root == NULL) {
TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
error.text);
goto fail;
} else if (!json_is_object(root)) {
TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
goto fail;
}
json_t *action = json_object_get(root, "action");
if (action == NULL) {
TELEMETRY_LOG_WARN("Request does not have action field");
goto fail;
} else if (!json_is_integer(action)) {
TELEMETRY_LOG_WARN("Action value is not an integer");
goto fail;
}
json_t *command = json_object_get(root, "command");
if (command == NULL) {
TELEMETRY_LOG_WARN("Request does not have command field");
goto fail;
} else if (!json_is_string(command)) {
TELEMETRY_LOG_WARN("Command value is not a string");
goto fail;
}
action_int = json_integer_value(action);
if (action_int != ACTION_POST) {
TELEMETRY_LOG_WARN("Invalid action code");
goto fail;
}
if (strcmp(json_string_value(command), "clients") != 0) {
TELEMETRY_LOG_WARN("Invalid command");
goto fail;
}
json_t *data = json_object_get(root, "data");
if (data == NULL) {
TELEMETRY_LOG_WARN("Request does not have data field");
goto fail;
}
json_t *client_path = json_object_get(data, "client_path");
if (client_path == NULL) {
TELEMETRY_LOG_WARN("Request does not have client_path field");
goto fail;
}
if (!json_is_string(client_path)) {
TELEMETRY_LOG_WARN("Client_path value is not a string");
goto fail;
}
ret = rte_telemetry_register_client(telemetry,
json_string_value(client_path));
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not register client");
telemetry->register_fail_count++;
goto fail;
}
return 0;
fail:
TELEMETRY_LOG_WARN("Client attempted to register with invalid message");
json_decref(root);
return -1;
}
static int32_t
rte_telemetry_dummy_client_socket(const char *valid_client_path)
{
int sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
struct sockaddr_un addr = {0};
if (sockfd < 0) {
TELEMETRY_LOG_ERR("Test socket creation failure");
return -1;
}
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path));
unlink(valid_client_path);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
TELEMETRY_LOG_ERR("Test socket binding failure");
return -1;
}
if (listen(sockfd, 1) < 0) {
TELEMETRY_LOG_ERR("Listen failure");
return -1;
}
return sockfd;
}
int32_t
rte_telemetry_selftest(void)
{
const char *invalid_client_path = SELFTEST_INVALID_CLIENT;
const char *valid_client_path = SELFTEST_VALID_CLIENT;
int ret, sockfd;
TELEMETRY_LOG_INFO("Selftest");
ret = rte_telemetry_init();
if (ret < 0) {
TELEMETRY_LOG_ERR("Valid initialisation test failed");
return -1;
}
TELEMETRY_LOG_INFO("Success - Valid initialisation test passed");
ret = rte_telemetry_init();
if (ret != -EALREADY) {
TELEMETRY_LOG_ERR("Invalid initialisation test failed");
return -1;
}
TELEMETRY_LOG_INFO("Success - Invalid initialisation test passed");
ret = rte_telemetry_unregister_client(static_telemetry,
invalid_client_path);
if (ret != -EPERM) {
TELEMETRY_LOG_ERR("Invalid unregister test failed");
return -1;
}
TELEMETRY_LOG_INFO("Success - Invalid unregister test passed");
sockfd = rte_telemetry_dummy_client_socket(valid_client_path);
if (sockfd < 0) {
TELEMETRY_LOG_ERR("Test socket creation failed");
return -1;
}
ret = rte_telemetry_register_client(static_telemetry, valid_client_path);
if (ret != 0) {
TELEMETRY_LOG_ERR("Valid register test failed: %i", ret);
return -1;
}
accept(sockfd, NULL, NULL);
TELEMETRY_LOG_INFO("Success - Valid register test passed");
ret = rte_telemetry_register_client(static_telemetry, valid_client_path);
if (ret != -EINVAL) {
TELEMETRY_LOG_ERR("Invalid register test failed: %i", ret);
return -1;
}
TELEMETRY_LOG_INFO("Success - Invalid register test passed");
ret = rte_telemetry_unregister_client(static_telemetry,
invalid_client_path);
if (ret != -1) {
TELEMETRY_LOG_ERR("Invalid unregister test failed: %i", ret);
return -1;
}
TELEMETRY_LOG_INFO("Success - Invalid unregister test passed");
ret = rte_telemetry_unregister_client(static_telemetry, valid_client_path);
if (ret != 0) {
TELEMETRY_LOG_ERR("Valid unregister test failed: %i", ret);
return -1;
}
TELEMETRY_LOG_INFO("Success - Valid unregister test passed");
ret = rte_telemetry_cleanup();
if (ret < 0) {
TELEMETRY_LOG_ERR("Cleanup test failed");
return -1;
}
TELEMETRY_LOG_INFO("Success - Valid cleanup test passed");
return 0;
}
int32_t
rte_telemetry_socket_messaging_testing(int index, int socket)
{
struct telemetry_impl *telemetry = calloc(1, sizeof(telemetry_impl));
int fd, bad_send_fd, send_fd, bad_fd, bad_recv_fd, recv_fd, ret;
if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Could not initialize Telemetry API");
return -1;
}
telemetry->server_fd = socket;
telemetry->reg_index[0] = index;
TELEMETRY_LOG_INFO("Beginning Telemetry socket message Selftest");
rte_telemetry_socket_test_setup(telemetry, &send_fd, &recv_fd);
TELEMETRY_LOG_INFO("Register valid client test");
ret = rte_telemetry_socket_register_test(telemetry, &fd, send_fd,
recv_fd);
if (ret < 0) {
TELEMETRY_LOG_ERR("Register valid client test failed!");
free(telemetry);
return -1;
}
TELEMETRY_LOG_INFO("Success - Register valid client test passed!");
TELEMETRY_LOG_INFO("Register invalid/same client test");
ret = rte_telemetry_socket_test_setup(telemetry, &bad_send_fd,
&bad_recv_fd);
ret = rte_telemetry_socket_register_test(telemetry, &bad_fd,
bad_send_fd, bad_recv_fd);
if (!ret) {
TELEMETRY_LOG_ERR("Register invalid/same client test failed!");
free(telemetry);
return -1;
}
TELEMETRY_LOG_INFO("Success - Register invalid/same client test passed!");
ret = rte_telemetry_json_socket_message_test(telemetry, fd);
if (ret < 0) {
free(telemetry);
return -1;
}
free(telemetry);
return 0;
}
int32_t
rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
int send_fd, int recv_fd)
{
int ret;
char good_req_string[BUF_SIZE];
snprintf(good_req_string, sizeof(good_req_string),
"{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\""
":\"%s\"}}", SOCKET_TEST_CLIENT_PATH);
listen(recv_fd, 1);
ret = send(send_fd, good_req_string, strlen(good_req_string), 0);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not send message over socket");
return -1;
}
rte_telemetry_run(telemetry);
if (telemetry->register_fail_count != 0)
return -1;
*fd = accept(recv_fd, NULL, NULL);
return 0;
}
int32_t
rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
int *recv_fd)
{
int ret;
const char *client_path = SOCKET_TEST_CLIENT_PATH;
char socket_path[BUF_SIZE];
struct sockaddr_un addr = {0};
struct sockaddr_un addrs = {0};
*send_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
*recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
listen(telemetry->server_fd, 5);
addr.sun_family = AF_UNIX;
rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not connect socket");
return -1;
}
telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
addrs.sun_family = AF_UNIX;
strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
unlink(client_path);
ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs));
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not bind socket");
return -1;
}
return 0;
}
static int32_t
rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct)
{
json_error_t error;
json_t *root = json_loads(buf, 0, &error);
int arraylen, i;
json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj,
*statsArrayObj;
stats = NULL;
port = NULL;
name = NULL;
if (buf == NULL) {
TELEMETRY_LOG_ERR("JSON message is NULL");
return -EINVAL;
}
if (root == NULL) {
TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s",
error.text);
return -EPERM;
} else if (!json_is_object(root)) {
TELEMETRY_LOG_ERR("JSON Request is not a JSON object");
json_decref(root);
return -EINVAL;
}
status = json_object_get(root, "status_code");
if (!status) {
TELEMETRY_LOG_ERR("Request does not have status field");
return -EINVAL;
} else if (!json_is_string(status)) {
TELEMETRY_LOG_ERR("Status value is not a string");
return -EINVAL;
}
json_data_struct->status_code = strdup(json_string_value(status));
dataArray = json_object_get(root, "data");
if (dataArray == NULL) {
TELEMETRY_LOG_ERR("Request does not have data field");
return -EINVAL;
}
arraylen = json_array_size(dataArray);
if (arraylen == 0) {
json_data_struct->data = "null";
return -EINVAL;
}
for (i = 0; i < arraylen; i++) {
dataArrayObj = json_array_get(dataArray, i);
port = json_object_get(dataArrayObj, "port");
stats = json_object_get(dataArrayObj, "stats");
}
if (port == NULL) {
TELEMETRY_LOG_ERR("Request does not have port field");
return -EINVAL;
}
if (!json_is_integer(port)) {
TELEMETRY_LOG_ERR("Port value is not an integer");
return -EINVAL;
}
json_data_struct->port = json_integer_value(port);
if (stats == NULL) {
TELEMETRY_LOG_ERR("Request does not have stats field");
return -EINVAL;
}
arraylen = json_array_size(stats);
for (i = 0; i < arraylen; i++) {
statsArrayObj = json_array_get(stats, i);
name = json_object_get(statsArrayObj, "name");
value = json_object_get(statsArrayObj, "value");
}
if (name == NULL) {
TELEMETRY_LOG_ERR("Request does not have name field");
return -EINVAL;
}
if (!json_is_string(name)) {
TELEMETRY_LOG_ERR("Stat name value is not a string");
return -EINVAL;
}
json_data_struct->stat_name = strdup(json_string_value(name));
if (value == NULL) {
TELEMETRY_LOG_ERR("Request does not have value field");
return -EINVAL;
}
if (!json_is_integer(value)) {
TELEMETRY_LOG_ERR("Stat value is not an integer");
return -EINVAL;
}
json_data_struct->stat_value = json_integer_value(value);
return 0;
}
static void
rte_telemetry_free_test_data(struct json_data *data)
{
free(data->status_code);
free(data->stat_name);
free(data);
}
int32_t
rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd)
{
int ret;
int port = 0;
int value = 0;
int fail_count = 0;
int buffer_read = 0;
char buf[BUF_SIZE];
struct json_data *data_struct;
errno = 0;
const char *status = "Status OK: 200";
const char *name = "rx_good_packets";
const char *valid_json_message = "{\"action\":0,\"command\":"
"\"ports_stats_values_by_name\",\"data\":{\"ports\""
":[0],\"stats\":[\"rx_good_packets\"]}}";
ret = send(fd, valid_json_message, strlen(valid_json_message), 0);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not send message over socket");
return -1;
}
rte_telemetry_run(telemetry);
buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
if (buffer_read == -1) {
TELEMETRY_LOG_ERR("Read error");
return -1;
}
buf[buffer_read] = '\0';
data_struct = calloc(1, sizeof(struct json_data));
ret = rte_telemetry_stat_parse(buf, data_struct);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not parse stats");
fail_count++;
}
if (strcmp(data_struct->status_code, status) != 0) {
TELEMETRY_LOG_ERR("Status code is invalid");
fail_count++;
}
if (data_struct->port != port) {
TELEMETRY_LOG_ERR("Port is invalid");
fail_count++;
}
if (strcmp(data_struct->stat_name, name) != 0) {
TELEMETRY_LOG_ERR("Stat name is invalid");
fail_count++;
}
if (data_struct->stat_value != value) {
TELEMETRY_LOG_ERR("Stat value is invalid");
fail_count++;
}
rte_telemetry_free_test_data(data_struct);
if (fail_count > 0)
return -1;
TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed");
return 0;
}
int32_t
rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd)
{
int ret;
char buf[BUF_SIZE];
int fail_count = 0;
const char *invalid_json = "{]";
const char *status = "Status Error: Unknown";
const char *data = "null";
struct json_data *data_struct;
int buffer_read = 0;
errno = 0;
ret = send(fd, invalid_json, strlen(invalid_json), 0);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not send message over socket");
return -1;
}
rte_telemetry_run(telemetry);
buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
if (buffer_read == -1) {
TELEMETRY_LOG_ERR("Read error");
return -1;
}
buf[buffer_read] = '\0';
data_struct = calloc(1, sizeof(struct json_data));
ret = rte_telemetry_stat_parse(buf, data_struct);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not parse stats");
if (strcmp(data_struct->status_code, status) != 0) {
TELEMETRY_LOG_ERR("Status code is invalid");
fail_count++;
}
if (strcmp(data_struct->data, data) != 0) {
TELEMETRY_LOG_ERR("Data status is invalid");
fail_count++;
}
rte_telemetry_free_test_data(data_struct);
if (fail_count > 0)
return -1;
TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test");
return 0;
}
int32_t
rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd)
{
int ret;
char buf[BUF_SIZE];
int fail_count = 0;
const char *status = "Status Error: Invalid Argument 404";
const char *data = "null";
struct json_data *data_struct;
const char *invalid_contents = "{\"action\":0,\"command\":"
"\"ports_stats_values_by_name\",\"data\":{\"ports\""
":[0],\"stats\":[\"some_invalid_param\","
"\"another_invalid_param\"]}}";
int buffer_read = 0;
errno = 0;
ret = send(fd, invalid_contents, strlen(invalid_contents), 0);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not send message over socket");
return -1;
}
rte_telemetry_run(telemetry);
buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
if (buffer_read == -1) {
TELEMETRY_LOG_ERR("Read error");
return -1;
}
buf[buffer_read] = '\0';
data_struct = calloc(1, sizeof(struct json_data));
ret = rte_telemetry_stat_parse(buf, data_struct);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not parse stats");
if (strcmp(data_struct->status_code, status) != 0) {
TELEMETRY_LOG_ERR("Status code is invalid");
fail_count++;
}
if (strcmp(data_struct->data, data) != 0) {
TELEMETRY_LOG_ERR("Data status is invalid");
fail_count++;
}
rte_telemetry_free_test_data(data_struct);
if (fail_count > 0)
return -1;
TELEMETRY_LOG_INFO("Success - Passed invalid JSON content test");
return 0;
}
int32_t
rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd)
{
int ret;
char buf[BUF_SIZE];
int fail_count = 0;
const char *status = "Status Error: Invalid Argument 404";
const char *data = "null";
struct json_data *data_struct;
const char *empty_json = "{}";
int buffer_read = 0;
errno = 0;
ret = (send(fd, empty_json, strlen(empty_json), 0));
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not send message over socket");
return -1;
}
rte_telemetry_run(telemetry);
buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
if (buffer_read == -1) {
TELEMETRY_LOG_ERR("Read error");
return -1;
}
buf[buffer_read] = '\0';
data_struct = calloc(1, sizeof(struct json_data));
ret = rte_telemetry_stat_parse(buf, data_struct);
if (ret < 0)
TELEMETRY_LOG_ERR("Could not parse stats");
if (strcmp(data_struct->status_code, status) != 0) {
TELEMETRY_LOG_ERR("Status code is invalid");
fail_count++;
}
if (strcmp(data_struct->data, data) != 0) {
TELEMETRY_LOG_ERR("Data status is invalid");
fail_count++;
}
rte_telemetry_free_test_data(data_struct);
if (fail_count > 0)
return -1;
TELEMETRY_LOG_INFO("Success - Passed JSON empty message test");
return 0;
}
int32_t
rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry, int fd)
{
uint16_t i;
int ret, fail_count;
fail_count = 0;
struct telemetry_message_test socket_json_tests[] = {
{.test_name = "Invalid JSON test",
.test_func_ptr = rte_telemetry_invalid_json_test},
{.test_name = "Valid JSON test",
.test_func_ptr = rte_telemetry_valid_json_test},
{.test_name = "JSON contents test",
.test_func_ptr = rte_telemetry_json_contents_test},
{.test_name = "JSON empty tests",
.test_func_ptr = rte_telemetry_json_empty_test}
};
#define NUM_TESTS RTE_DIM(socket_json_tests)
for (i = 0; i < NUM_TESTS; i++) {
TELEMETRY_LOG_INFO("%s", socket_json_tests[i].test_name);
ret = (socket_json_tests[i].test_func_ptr)
(telemetry, fd);
if (ret < 0) {
TELEMETRY_LOG_ERR("%s failed",
socket_json_tests[i].test_name);
fail_count++;
}
}
if (fail_count > 0) {
TELEMETRY_LOG_ERR("Failed %i JSON socket message test(s)",
fail_count);
return -1;
}
TELEMETRY_LOG_INFO("Success - All JSON tests passed");
return 0;
}
int telemetry_log_level;
static struct rte_option option = {
.name = "telemetry",
.usage = "Enable telemetry backend",
.cb = &rte_telemetry_init,
.enabled = 0
};
RTE_INIT(rte_telemetry_register)
{
telemetry_log_level = rte_log_register("lib.telemetry");
if (telemetry_log_level >= 0)
rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
rte_option_register(&option);
}