Discussion:
[dpdk-dev] [PATCH 00/11] introduce telemetry library
Ciara Power
2018-08-23 12:08:02 UTC
Permalink
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.

The telemetry library provides a method for a service assurance component
to retrieve metrics from a DPDK packet forwarding application.
Communicating from the service assurance component to DPDK is done using
a UNIX domain socket, passing a JSON formatted string. A reply is
sent (again a JSON formatted string) of the current DPDK metrics.

The telemetry component makes use of the existing rte_metrics library to
query values. The values to be transmitted via the telemetry infrastructure
must be present in the Metrics library. Currently the ethdev values are
pushed to the metrics library, and the queried from there – there is an
open question on how applications would like this to occur. Currently only
ethdev to metrics functionality is implemented, however other subsystems
like crypto, eventdev, keepalive etc can use similar mechanisms.

Exposing DPDK Telemetry via a socket interface enables service assurance
agents like collectd to consume data from DPDK. This is vital for
monitoring, fault-detection, and error reporting. A collectd plugin has
been created to interact with the DPDK Telemetry component, showing how it
can be used in practice. The collectd plugin patch can be found at
lib/librte_telemetry/collectd_plugin/v1-0001-dpdk_telemetry-add-plugin-
for-DPDK-metrics-via-DP.patch, this will be upstreamed to collectd at a
later stage. Further instructions on applying the collectd patch can be
found in the collectd patch commit message. A small python script is
provided in ./usertools/telemetry_client.py to quick-start using
DPDK Telemetry.

Ciara Power and Brian Archbold (10):
telemetry: initial telemetry infrastructure
telemetry: add initial connection socket
telemetry: add client feature and sockets
telemetry: add parser for client socket messages
telemetry: update metrics before sending stats
telemetry: format json response when sending stats
telemetry: add tests for telemetry api
telemetry: add vdev kvargs for selftest
doc: add telemetry documentation
usertools: add client python script for telemetry

Emma Kenny (1) :
telemetry: add collectd plugin patch

config/common_base | 5 +
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 86 +
drivers/Makefile | 3 +
drivers/meson.build | 3 +-
drivers/telemetry/Makefile | 8 +
drivers/telemetry/meson.build | 6 +
drivers/telemetry/telemetry/Makefile | 26 +
drivers/telemetry/telemetry/meson.build | 5 +
.../telemetry/rte_pmd_telemetry_version.map | 9 +
drivers/telemetry/telemetry/telemetry_driver.c | 83 +
lib/Makefile | 2 +
lib/librte_telemetry/Makefile | 28 +
...emetry-add-plugin-for-DPDK-metrics-via-DP.patch | 578 +++++++
lib/librte_telemetry/meson.build | 9 +
lib/librte_telemetry/rte_telemetry.c | 1737 ++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 58 +
lib/librte_telemetry/rte_telemetry_internal.h | 72 +
lib/librte_telemetry/rte_telemetry_parser.c | 604 +++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 574 +++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 +
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 +
lib/librte_telemetry/rte_telemetry_version.map | 7 +
lib/meson.build | 2 +-
mk/rte.app.mk | 2 +
usertools/telemetry_client.py | 116 ++
27 files changed, 4110 insertions(+), 2 deletions(-)
create mode 100644 doc/guides/howto/telemetry.rst
create mode 100644 drivers/telemetry/Makefile
create mode 100644 drivers/telemetry/meson.build
create mode 100644 drivers/telemetry/telemetry/Makefile
create mode 100644 drivers/telemetry/telemetry/meson.build
create mode 100644 drivers/telemetry/telemetry/rte_pmd_telemetry_version.map
create mode 100644 drivers/telemetry/telemetry/telemetry_driver.c
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/collectd_plugin/v1-0001-dpdk_telemetry-add-plugin-for-DPDK-metrics-via-DP.patch
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map
create mode 100644 usertools/telemetry_client.py
--
2.7.4
Ciara Power
2018-08-23 12:08:03 UTC
Permalink
This patch adds the infrastructure and initial code for the
telemetry library.

A virtual device is used for telemetry, which is included in
this patch. To use telemetry, a command-line argument must be
used - "--vdev=telemetry".

Control threads are used to get CPU cycles for telemetry, which
are configured in this patch also.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
config/common_base | 5 +
drivers/Makefile | 3 +
drivers/meson.build | 3 +-
drivers/telemetry/Makefile | 8 ++
drivers/telemetry/meson.build | 6 ++
drivers/telemetry/telemetry/Makefile | 26 +++++
drivers/telemetry/telemetry/meson.build | 5 +
.../telemetry/rte_pmd_telemetry_version.map | 9 ++
drivers/telemetry/telemetry/telemetry_driver.c | 40 ++++++++
lib/Makefile | 2 +
lib/librte_telemetry/Makefile | 26 +++++
lib/librte_telemetry/meson.build | 7 ++
lib/librte_telemetry/rte_telemetry.c | 108 +++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 40 ++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 32 ++++++
lib/librte_telemetry/rte_telemetry_version.map | 6 ++
lib/meson.build | 2 +-
mk/rte.app.mk | 2 +
18 files changed, 328 insertions(+), 2 deletions(-)
create mode 100644 drivers/telemetry/Makefile
create mode 100644 drivers/telemetry/meson.build
create mode 100644 drivers/telemetry/telemetry/Makefile
create mode 100644 drivers/telemetry/telemetry/meson.build
create mode 100644 drivers/telemetry/telemetry/rte_pmd_telemetry_version.map
create mode 100644 drivers/telemetry/telemetry/telemetry_driver.c
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map

diff --git a/config/common_base b/config/common_base
index 4bcbaf9..682f8bf 100644
--- a/config/common_base
+++ b/config/common_base
@@ -716,6 +716,11 @@ CONFIG_RTE_LIBRTE_HASH=y
CONFIG_RTE_LIBRTE_HASH_DEBUG=n

#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+#
# Compile librte_efd
#
CONFIG_RTE_LIBRTE_EFD=y
diff --git a/drivers/Makefile b/drivers/Makefile
index 7566076..2238290 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -21,5 +21,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += event
DEPDIRS-event := common bus mempool net
DIRS-$(CONFIG_RTE_LIBRTE_RAWDEV) += raw
DEPDIRS-raw := common bus mempool net event
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += telemetry
+DEPDIRS-telemetry := common bus mempool
+

include $(RTE_SDK)/mk/rte.subdir.mk
diff --git a/drivers/meson.build b/drivers/meson.build
index f94e2fe..d38f22f 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -9,7 +9,8 @@ driver_classes = ['common',
'crypto', # depends on common, bus and mempool (net in future).
'compress', # depends on common, bus, mempool.
'event', # depends on common, bus, mempool and net.
- 'raw'] # depends on common, bus, mempool, net and event.
+ 'raw', # depends on common, bus, mempool, net and event.
+ 'telemetry'] # depends on common, bus, and mempool.

default_cflags = machine_args
if cc.has_argument('-Wno-format-truncation')
diff --git a/drivers/telemetry/Makefile b/drivers/telemetry/Makefile
new file mode 100644
index 0000000..86b0342
--- /dev/null
+++ b/drivers/telemetry/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += telemetry
+
+include $(RTE_SDK)/mk/rte.subdir.mk
diff --git a/drivers/telemetry/meson.build b/drivers/telemetry/meson.build
new file mode 100644
index 0000000..b7eb791
--- /dev/null
+++ b/drivers/telemetry/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+drivers = ['telemetry']
+config_flag_fmt = '***@0@_PMD'
+driver_name_fmt = '***@0@'
diff --git a/drivers/telemetry/telemetry/Makefile b/drivers/telemetry/telemetry/Makefile
new file mode 100644
index 0000000..e9d98f1
--- /dev/null
+++ b/drivers/telemetry/telemetry/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_pmd_telemetry.a
+
+# build flags
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+LDLIBS += -lrte_eal -lrte_mbuf
+LDLIBS += -lrte_telemetry
+LDLIBS += -lrte_bus_vdev
+
+# versioning export map
+EXPORT_MAP := rte_pmd_telemetry_version.map
+
+# library version
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := telemetry_driver.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/telemetry/telemetry/meson.build b/drivers/telemetry/telemetry/meson.build
new file mode 100644
index 0000000..baebcab
--- /dev/null
+++ b/drivers/telemetry/telemetry/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('telemetry_driver.c')
+deps += ['telemetry', 'bus_pci', 'bus_vdev']
diff --git a/drivers/telemetry/telemetry/rte_pmd_telemetry_version.map b/drivers/telemetry/telemetry/rte_pmd_telemetry_version.map
new file mode 100644
index 0000000..a73e0f2
--- /dev/null
+++ b/drivers/telemetry/telemetry/rte_pmd_telemetry_version.map
@@ -0,0 +1,9 @@
+DPDK_18.05 {
+ global:
+
+ telemetry_probe;
+ telemetry_remove;
+
+ local: *;
+
+};
diff --git a/drivers/telemetry/telemetry/telemetry_driver.c b/drivers/telemetry/telemetry/telemetry_driver.c
new file mode 100644
index 0000000..c56f60c
--- /dev/null
+++ b/drivers/telemetry/telemetry/telemetry_driver.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+
+#include <rte_telemetry.h>
+#include <rte_malloc.h>
+#include <rte_bus_vdev.h>
+#include <rte_lcore.h>
+
+static int
+telemetry_probe(struct rte_vdev_device *vdev)
+{
+ int ret;
+
+ RTE_SET_USED(vdev);
+ ret = rte_telemetry_init(rte_socket_id());
+ if (ret < 0) {
+ printf("Error - Telemetry initialisation failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+telemetry_remove(struct rte_vdev_device *vdev)
+{
+ const char *name;
+ name = rte_vdev_device_name(vdev);
+ printf("Uninitialising the device: %s\n", name);
+ return 0;
+}
+
+static struct rte_vdev_driver pmd_telemetry_drv = {
+ .probe = telemetry_probe,
+ .remove = telemetry_remove
+};
+
+RTE_PMD_REGISTER_VDEV(telemetry, pmd_telemetry_drv);
diff --git a/lib/Makefile b/lib/Makefile
index afa604e..8cbd035 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev

ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..bda3788
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
new file mode 100644
index 0000000..8d7b0e3
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
+ if (!telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY could not be "
+ "initialised\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+*rte_telemetry_run_thread_func(void *userdata)
+{
+ int ret;
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - %s passed a NULL instance\n",
+ __func__);
+ pthread_exit(0);
+ }
+
+ while (telemetry->thread_status) {
+ rte_telemetry_run(telemetry);
+ ret = usleep(SLEEP_TIME);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Calling thread could not be"
+ " put to sleep\n");
+ }
+ pthread_exit(0);
+}
+
+int32_t
+rte_telemetry_init(uint32_t socket_id)
+{
+ int ret;
+ pthread_attr_t attr;
+ const char *telemetry_ctrl_thread = "telemetry";
+
+ if (static_telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY structure already "
+ "initialised\n");
+ return -EALREADY;
+ }
+
+ static_telemetry = calloc(1, sizeof(struct telemetry_impl));
+ if (!static_telemetry) {
+ TELEMETRY_LOG_ERR("Error - Memory could not be allocated\n");
+ return -ENOMEM;
+ }
+
+ static_telemetry->socket_id = socket_id;
+ rte_metrics_init(static_telemetry->socket_id);
+ pthread_attr_init(&attr);
+ 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("Error - TELEMETRY cleanup failed\n");
+ return -EPERM;
+ }
+ return 0;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_log_init);
+
+static void
+rte_telemetry_log_init(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 0000000..b691845
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * Get the telemetry_impl structure device pointer initialised.
+ *
+ * @param socket_id
+ * Unsigned integer representing the socket id to be used
+ * for the telemetry structure.
+ *
+ * @return
+ * 0 on successful initialisation.
+ * @return
+ * -ENOMEM on memory allocation error
+ * @return
+ * -EPERM on unknown error failure
+ * @return
+ * -EALREADY if Telemetry is already initialised.
+ */
+int32_t
+rte_telemetry_init(uint32_t socket_id);
+
+/**
+ * Clean up and free memory.
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -EPERM on failure
+ */
+int32_t
+rte_telemetry_cleanup(void);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
new file mode 100644
index 0000000..4e810a8
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <rte_log.h>
+
+#ifndef _RTE_TELEMETRY_INTERNAL_H_
+#define _RTE_TELEMETRY_INTERNAL_H_
+
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
+ uint32_t socket_id;
+} telemetry_impl;
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
new file mode 100644
index 0000000..efd437d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.05 {
+ global:
+
+ rte_telemetry_init;
+ local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index eb91f10..fc84b2f 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -24,7 +24,7 @@ libraries = [ 'compat', # just a header, used for versioning
# add pkt framework libs which use other libs from above
'port', 'table', 'pipeline',
# flow_classify lib depends on pkt framework table lib
- 'flow_classify', 'bpf']
+ 'flow_classify', 'bpf', 'telemetry']

default_cflags = machine_args
if cc.has_argument('-Wno-format-truncation')
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index de33883..8551191 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,6 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
@@ -164,6 +165,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_SOFTNIC) += -lrte_pmd_softnic
endif
_LDLIBS-$(CONFIG_RTE_LIBRTE_SFC_EFX_PMD) += -lrte_pmd_sfc_efx
_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_SZEDATA2) += -lrte_pmd_szedata2 -lsze2
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_pmd_telemetry
_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += -lrte_pmd_tap
_LDLIBS-$(CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD) += -lrte_pmd_thunderx_nicvf
_LDLIBS-$(CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD) += -lrte_pmd_vdev_netvsc
--
2.7.4
Stephen Hemminger
2018-08-23 23:17:06 UTC
Permalink
On Thu, 23 Aug 2018 13:08:03 +0100
Post by Ciara Power
+
+static int
+telemetry_probe(struct rte_vdev_device *vdev)
+{
+ int ret;
+
+ RTE_SET_USED(vdev);
+ ret = rte_telemetry_init(rte_socket_id());
+ if (ret < 0) {
+ printf("Error - Telemetry initialisation failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+telemetry_remove(struct rte_vdev_device *vdev)
+{
+ const char *name;
+ name = rte_vdev_device_name(vdev);
+ printf("Uninitialising the device: %s\n", name);
Please use DPDK (rte) logging for all messages.
Stephen Hemminger
2018-08-23 23:17:49 UTC
Permalink
On Thu, 23 Aug 2018 13:08:03 +0100
Post by Ciara Power
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
Casting a void * pointer is unnecessary in C.
Assignment has an implicit cast.
Stephen Hemminger
2018-08-23 23:18:55 UTC
Permalink
On Thu, 23 Aug 2018 13:08:03 +0100
Post by Ciara Power
+ TELEMETRY_LOG_ERR("Error - Calling thread could not be"
+ " put to sleep\n");
Don't break strings across lines. It makes it harder for developers who want
to use tools to look for log message in source. Checkpatch and friends allow for long
line exception for string literals.
Stephen Hemminger
2018-08-23 23:19:46 UTC
Permalink
On Thu, 23 Aug 2018 13:08:03 +0100
Post by Ciara Power
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * Get the telemetry_impl structure device pointer initialised.
+ *
+ * Unsigned integer representing the socket id to be used
+ * for the telemetry structure.
+ *
+ * 0 on successful initialisation.
+ * -ENOMEM on memory allocation error
+ * -EPERM on unknown error failure
+ * -EALREADY if Telemetry is already initialised.
+ */
+int32_t
+rte_telemetry_init(uint32_t socket_id);
+
+/**
+ * Clean up and free memory.
+ *
+ * 0 on success
+ * -EPERM on failure
+ */
+int32_t
+rte_telemetry_cleanup(void);
+
Can this be done with RTE_INIT (i.e automatic constructor).
Stephen Hemminger
2018-08-23 23:22:22 UTC
Permalink
On Thu, 23 Aug 2018 13:08:03 +0100
Post by Ciara Power
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
+ uint32_t socket_id;
+} telemetry_impl;
+
Your logging macros follow the standard DPDK style. Including automatically
adding a new line. But as I look at the code, many of the TELEMETRY_LOG calls
have a newline in the format. Therefore your log messages will be double spaced.
Van Haaren, Harry
2018-08-28 17:12:36 UTC
Permalink
Sent: Friday, August 24, 2018 12:22 AM
Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
infrastructure
On Thu, 23 Aug 2018 13:08:03 +0100
Post by Ciara Power
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
+ uint32_t socket_id;
+} telemetry_impl;
+
Your logging macros follow the standard DPDK style. Including automatically
adding a new line. But as I look at the code, many of the TELEMETRY_LOG calls
have a newline in the format. Therefore your log messages will be double spaced.
Correct, will get that fixed.

Thanks for reviewing! Looking forward to Userspace, curious to hear of
your use-cases for Telemetry lib, assuming you have some in mind.
Shreyansh Jain
2018-08-24 13:03:43 UTC
Permalink
Post by Ciara Power
This patch adds the infrastructure and initial code for the
telemetry library.
A virtual device is used for telemetry, which is included in
this patch. To use telemetry, a command-line argument must be
used - "--vdev=telemetry".
Control threads are used to get CPU cycles for telemetry, which
are configured in this patch also.
---
[...]

/rte_pmd_telemetry_version.map
Post by Ciara Power
new file mode 100644
index 0000000..a73e0f2
--- /dev/null
+++ b/drivers/telemetry/telemetry/rte_pmd_telemetry_version.map
@@ -0,0 +1,9 @@
+DPDK_18.05 {
^^^^^
I think you want 18.11 here.
Post by Ciara Power
+
+ telemetry_probe;
+ telemetry_remove;
+
+ local: *;
+
+};
[...]
Post by Ciara Power
diff --git a/lib/Makefile b/lib/Makefile
index afa604e..8cbd035 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..bda3788
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
new file mode 100644
index 0000000..8d7b0e3
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
+ if (!telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY could not be "
+ "initialised\n");
Your 'TELEMETRY_LOG_WARNING' already includes a '\n' in its definition.
This would add another one. Can you re-check?
Post by Ciara Power
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+*rte_telemetry_run_thread_func(void *userdata)
+{
+ int ret;
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - %s passed a NULL instance\n",
+ __func__);
I might be picky - but this is an internal function spawned using
rte_ctrl_thread_create which already has a check whether the argument
(static_telemetry) is NULL or not. So, this is like duplicating that work.
Post by Ciara Power
+ pthread_exit(0);
+ }
+
+ while (telemetry->thread_status) {
+ rte_telemetry_run(telemetry);
+ ret = usleep(SLEEP_TIME);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Calling thread could not be"
+ " put to sleep\n");
If the calling thread couldn't be put to sleep, you would continue
looping without sleeping? Wouldn't that simply hog your CPU? Or, is that
expected design?
Post by Ciara Power
+ }
+ pthread_exit(0);
+}
+
+int32_t
+rte_telemetry_init(uint32_t socket_id)
+{
+ int ret;
+ pthread_attr_t attr;
+ const char *telemetry_ctrl_thread = "telemetry";
+
+ if (static_telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY structure already "
+ "initialised\n");
+ return -EALREADY;
+ }
+
+ static_telemetry = calloc(1, sizeof(struct telemetry_impl));
+ if (!static_telemetry) {
+ TELEMETRY_LOG_ERR("Error - Memory could not be allocated\n");
+ return -ENOMEM;
+ }
+
+ static_telemetry->socket_id = socket_id;
+ rte_metrics_init(static_telemetry->socket_id);
+ pthread_attr_init(&attr);
+ 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("Error - TELEMETRY cleanup failed\n");
+ return -EPERM;
+ }
+ return 0;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
Maybe if you could use be a little more liberal with new lines, it would
be slightly easier to read.
But again, that is my personal opinion.
Post by Ciara Power
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_log_init);
+
+static void
+rte_telemetry_log_init(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+}
[...]
Post by Ciara Power
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
new file mode 100644
index 0000000..efd437d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.05 {
^^^^^^
I think you want it to be 18.11 here.
Post by Ciara Power
+
+ rte_telemetry_init;
+ local: *;
+};
[...]
Van Haaren, Harry
2018-08-28 16:50:00 UTC
Permalink
Sent: Friday, August 24, 2018 2:04 PM
Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
infrastructure
Post by Ciara Power
This patch adds the infrastructure and initial code for the
telemetry library.
A virtual device is used for telemetry, which is included in
this patch. To use telemetry, a command-line argument must be
used - "--vdev=telemetry".
Control threads are used to get CPU cycles for telemetry, which
are configured in this patch also.
---
[...]
/rte_pmd_telemetry_version.map
Post by Ciara Power
new file mode 100644
index 0000000..a73e0f2
--- /dev/null
+++ b/drivers/telemetry/telemetry/rte_pmd_telemetry_version.map
@@ -0,0 +1,9 @@
+DPDK_18.05 {
^^^^^
I think you want 18.11 here.
Yes indeed.
Post by Ciara Power
+
+ telemetry_probe;
+ telemetry_remove;
+
+ local: *;
+
+};
[...]
Post by Ciara Power
diff --git a/lib/Makefile b/lib/Makefile
index afa604e..8cbd035 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf
librte_ethdev librte_net
Post by Ciara Power
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf
librte_ethdev
Post by Ciara Power
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..bda3788
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build
b/lib/librte_telemetry/meson.build
Post by Ciara Power
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c
b/lib/librte_telemetry/rte_telemetry.c
Post by Ciara Power
new file mode 100644
index 0000000..8d7b0e3
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
+ if (!telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY could not be "
+ "initialised\n");
Your 'TELEMETRY_LOG_WARNING' already includes a '\n' in its definition.
This would add another one. Can you re-check?
Yes, as Stephen noted too. Will fix!
Post by Ciara Power
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+*rte_telemetry_run_thread_func(void *userdata)
+{
+ int ret;
+ struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - %s passed a NULL instance\n",
+ __func__);
I might be picky - but this is an internal function spawned using
rte_ctrl_thread_create which already has a check whether the argument
(static_telemetry) is NULL or not. So, this is like duplicating that work.
Post by Ciara Power
+ pthread_exit(0);
+ }
+
+ while (telemetry->thread_status) {
+ rte_telemetry_run(telemetry);
+ ret = usleep(SLEEP_TIME);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Calling thread could not be"
+ " put to sleep\n");
If the calling thread couldn't be put to sleep, you would continue
looping without sleeping? Wouldn't that simply hog your CPU? Or, is that
expected design?
Will look into this.
Post by Ciara Power
+ }
+ pthread_exit(0);
+}
+
+int32_t
+rte_telemetry_init(uint32_t socket_id)
+{
+ int ret;
+ pthread_attr_t attr;
+ const char *telemetry_ctrl_thread = "telemetry";
+
+ if (static_telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY structure already "
+ "initialised\n");
+ return -EALREADY;
+ }
+
+ static_telemetry = calloc(1, sizeof(struct telemetry_impl));
+ if (!static_telemetry) {
+ TELEMETRY_LOG_ERR("Error - Memory could not be allocated\n");
+ return -ENOMEM;
+ }
+
+ static_telemetry->socket_id = socket_id;
+ rte_metrics_init(static_telemetry->socket_id);
+ pthread_attr_init(&attr);
+ 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("Error - TELEMETRY cleanup failed\n");
+ return -EPERM;
+ }
+ return 0;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
Maybe if you could use be a little more liberal with new lines, it would
be slightly easier to read.
But again, that is my personal opinion.
Thanks for the input, will be kept in mind.
Post by Ciara Power
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_log_init);
+
+static void
+rte_telemetry_log_init(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+}
[...]
Post by Ciara Power
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map
b/lib/librte_telemetry/rte_telemetry_version.map
Post by Ciara Power
new file mode 100644
index 0000000..efd437d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.05 {
^^^^^^
I think you want it to be 18.11 here.
Correct.

Thanks for review, hopefully see you at Userspace where
there will be a ligh
Gaëtan Rivet
2018-08-28 11:46:33 UTC
Permalink
Hi Ciara,
Post by Ciara Power
This patch adds the infrastructure and initial code for the
telemetry library.
A virtual device is used for telemetry, which is included in
this patch. To use telemetry, a command-line argument must be
used - "--vdev=telemetry".
Why use a virtual device?

It seems that you are only using the device semantic as a way to carry
around a tag telling the DPDK framework to init your library once it has
finished its initialization.

I guess you wanted to avoid having to add the call to rte_telemetry_init
to all applications. In the absence of a proper EAL option framework,
you can workaround by adding a --telemetry EAL parameter, setting a flag
on, and checking this flag from librte_telemetry, within a routine
declared with RTE_INIT_PRIO.

I only see afterward the selftest being triggered via kvargs. I haven't
yet looked at the testing done, but if it is only unit test, the "test"
app would be better suited. If it is integration testing to verify the
behavior of the library with other PMDs, you probably need specific
context, thus selftest being insufficient on its own and useless for
other users.
Post by Ciara Power
Control threads are used to get CPU cycles for telemetry, which
are configured in this patch also.
Regards,
--
Gaëtan Rivet
6WIND
Van Haaren, Harry
2018-08-28 16:54:33 UTC
Permalink
Hi Gaetan,
-----Original Message-----
Sent: Tuesday, August 28, 2018 12:47 PM
Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
infrastructure
Hi Ciara,
Post by Ciara Power
This patch adds the infrastructure and initial code for the
telemetry library.
A virtual device is used for telemetry, which is included in
this patch. To use telemetry, a command-line argument must be
used - "--vdev=telemetry".
Why use a virtual device?
It seems that you are only using the device semantic as a way to carry
around a tag telling the DPDK framework to init your library once it has
finished its initialization.
I guess you wanted to avoid having to add the call to rte_telemetry_init
to all applications. In the absence of a proper EAL option framework,
you can workaround by adding a --telemetry EAL parameter, setting a flag
on, and checking this flag from librte_telemetry, within a routine
declared with RTE_INIT_PRIO.
I suppose that an EAL --flag could work too, it would mean that EAL would
depend on this library. The --vdev trick keeps the library standalone.

I don't have a strong opinion either way. :)
I only see afterward the selftest being triggered via kvargs. I haven't
yet looked at the testing done, but if it is only unit test, the "test"
app would be better suited. If it is integration testing to verify the
behavior of the library with other PMDs, you probably need specific
context, thus selftest being insufficient on its own and useless for
other users.
Correct, self tests are triggered by kvargs. This same model is used
in eg: eventdev PMDs to run selftests, where the tests are pretty complex
and specific to the device under test.

Again, I don't have a strong opinion but I don't see any issue with it
being included in the vdev / telemetry library. We could write a shim
test that the "official" test binary runs the telemetry tests if that is
your concern?
Post by Ciara Power
Control threads are used to get CPU cycles for telemetry, which
are configured in this patch also.
Regards,
--
Gaëtan Rivet
6WIND
Thanks for review, and there's a lightning talk at Userspace so please
do provide input there too :) -Harry
Gaëtan Rivet
2018-08-29 08:23:11 UTC
Permalink
Post by Van Haaren, Harry
Hi Gaetan,
-----Original Message-----
Sent: Tuesday, August 28, 2018 12:47 PM
Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
infrastructure
Hi Ciara,
Post by Ciara Power
This patch adds the infrastructure and initial code for the
telemetry library.
A virtual device is used for telemetry, which is included in
this patch. To use telemetry, a command-line argument must be
used - "--vdev=telemetry".
Why use a virtual device?
It seems that you are only using the device semantic as a way to carry
around a tag telling the DPDK framework to init your library once it has
finished its initialization.
I guess you wanted to avoid having to add the call to rte_telemetry_init
to all applications. In the absence of a proper EAL option framework,
you can workaround by adding a --telemetry EAL parameter, setting a flag
on, and checking this flag from librte_telemetry, within a routine
declared with RTE_INIT_PRIO.
I suppose that an EAL --flag could work too, it would mean that EAL would
depend on this library. The --vdev trick keeps the library standalone.
I don't have a strong opinion either way. :)
This was done already for specific EAL configuration items such as
vfio intr_mode or PCI uio configuration.

Of course this is ugly, but the --telemetry parameter can exist without
compiling the lib. You can add a warning if the TELEMETRY Mconfig
item is not set to mitigate. The main issue is that you need to add
getters because you cannot declare an external *struct internal_config*
reference.

I agree this is awkward, and this is exactly the reason we need a
way for libraries to register options in the EAL, but this is not
yet done.

The virtual device solution however is a crutch used to emulate this
absent framework. This will complicate developping the proper solution
and its adoption once done. I would not be clear then to the dev that they
can translate the telemetry shim parameter to the new framework, without
having to rework the whole infrastructure of the lib (and this is without
talking about reworking the build system to remove the telemetry driver).

Even having to add a new driver subsection only for telemetry is awkward.

So we might certainly wait for second or third opinions, but I am firmly
convinced it would be easier in order to maintain the project (both from EAL
and systems standpoint and library standpoint) without the vdev trick.
Post by Van Haaren, Harry
I only see afterward the selftest being triggered via kvargs. I haven't
yet looked at the testing done, but if it is only unit test, the "test"
app would be better suited. If it is integration testing to verify the
behavior of the library with other PMDs, you probably need specific
context, thus selftest being insufficient on its own and useless for
other users.
Correct, self tests are triggered by kvargs. This same model is used
in eg: eventdev PMDs to run selftests, where the tests are pretty complex
and specific to the device under test.
Again, I don't have a strong opinion but I don't see any issue with it
being included in the vdev / telemetry library. We could write a shim
test that the "official" test binary runs the telemetry tests if that is
your concern?
Okay, I have no strong opinion about this (actually I prefer having the
test code close to the code-under-test), but eventdev can spawn device
objects to drive the test and provide configuration.

It would be more complicated using the same logic with a pure library,
without the vdev.
Post by Van Haaren, Harry
Post by Ciara Power
Control threads are used to get CPU cycles for telemetry, which
are configured in this patch also.
Regards,
--
Gaëtan Rivet
6WIND
Thanks for review, and there's a lightning talk at Userspace so please
do provide input there too :) -Harry
--
Gaëtan Rivet
6WIND
Ciara Power
2018-08-23 12:08:04 UTC
Permalink
This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.

On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 205 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
2 files changed, 209 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 8d7b0e3..f984929 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,21 +3,159 @@
*/

#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
+#include <rte_string_fns.h>

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

#define SLEEP_TIME 10

+#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+
+const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;

+int32_t
+rte_telemetry_check_port_activity(int port_id)
+{
+ int pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ if (pid == port_id)
+ return 1;
+ }
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid, not active\n",
+ port_id);
+ return 0;
+}
+
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
+{
+ int ret, num_xstats, start_index, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid\n", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) failed:"
+ " %d\n", port_id, num_xstats);
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for"
+ " xstats\n");
+ return -ENOMEM;
+ }
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) len%i"
+ " failed: %d\n", port_id, num_xstats, ret);
+ return -EPERM;
+ }
+ struct rte_eth_xstat_name *eth_xstats_names;
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) *
+ num_xstats);
+ if (eth_xstats_names == NULL) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for"
+ " xstats_names\n");
+ return -ENOMEM;
+ }
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names,
+ num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ free(eth_xstats_names);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get_names(%u)"
+ " len%i failed: %d\n", port_id, num_xstats,
+ ret);
+ return -EPERM;
+ }
+ const char *xstats_names[num_xstats];
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ start_index = rte_metrics_reg_names(xstats_names, num_xstats);
+
+ if (start_index < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_metrics_reg_names failed -"
+ " metrics may already be registered\n");
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return -1;
+ }
+ free(eth_xstats_names);
+ free(eth_xstats);
+ return start_index;
+}
+
+static int32_t
+rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
+{
+ int pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ telemetry->reg_index =
+ rte_telemetry_reg_ethdev_to_metrics(pid);
+ break;
+ }
+
+ if (telemetry->reg_index < 0) {
+ TELEMETRY_LOG_ERR("Error - failed to register ethdev "
+ "metrics\n");
+ return -1;
+ }
+ telemetry->metrics_register_done = 1;
+
+ return 0;
+}
+
+static int32_t
+rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
+{
+ int ret;
+ if (telemetry->accept_fd == 0 || telemetry->accept_fd == -1) {
+ ret = listen(telemetry->server_fd, 1);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Listening error with "
+ "server fd\n");
+ 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("Error - Failed to run "
+ "initial configurations/tests\n");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
static int32_t
rte_telemetry_run(void *userdata)
{
+ int ret;
struct telemetry_impl *telemetry = (struct telemetry_impl *)userdata;
if (!telemetry) {
TELEMETRY_LOG_WARN("Warning - TELEMETRY could not be "
@@ -25,6 +163,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_accept_new_client(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Accept and read new client "
+ "failed\n");
+ return -1;
+ }
return 0;
}

@@ -50,6 +194,51 @@ static void
pthread_exit(0);
}

+static int32_t
+rte_telemetry_set_socket_nonblock(int fd)
+{
+ if (fd < 0) {
+ TELEMETRY_LOG_ERR("Error - Invalid fd provided\n");
+ return -1;
+ }
+
+ int 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;
+
+ if (!telemetry)
+ return -1;
+
+ telemetry->server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (telemetry->server_fd == -1) {
+ TELEMETRY_LOG_ERR("Error - Failed to open socket\n");
+ return -1;
+ }
+ ret = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not set socket to NONBLOCK\n");
+ return -1;
+ }
+ struct sockaddr_un addr = {0};
+ addr.sun_family = AF_UNIX;
+ 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("Error - Socket binding error\n");
+ return -1;
+ }
+ return 0;
+}
+
int32_t
rte_telemetry_init(uint32_t socket_id)
{
@@ -71,6 +260,14 @@ rte_telemetry_init(uint32_t socket_id)

static_telemetry->socket_id = socket_id;
rte_metrics_init(static_telemetry->socket_id);
+ ret = rte_telemetry_create_socket(static_telemetry);
+ if (ret < 0) {
+ ret = rte_telemetry_cleanup();
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - TELEMETRY cleanup failed\n");
+ return -EPERM;
+ }
+
pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -88,7 +285,15 @@ rte_telemetry_init(uint32_t socket_id)
int32_t
rte_telemetry_cleanup(void)
{
+ int ret;
struct telemetry_impl *telemetry = static_telemetry;
+
+ ret = close(telemetry->server_fd);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Close TELEMETRY socket failed\n");
+ free(telemetry);
+ return -EPERM;
+ }
telemetry->thread_status = 0;
pthread_join(telemetry->thread_id, NULL);
free(telemetry);
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 4e810a8..569d56a 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -24,9 +24,13 @@ extern int telemetry_log_level;
TELEMETRY_LOG(INFO, fmt, ## args)

typedef struct telemetry_impl {
+ int accept_fd;
+ int server_fd;
pthread_t thread_id;
int thread_status;
uint32_t socket_id;
+ int reg_index;
+ int metrics_register_done;
} telemetry_impl;

#endif
--
2.7.4
Gaëtan Rivet
2018-08-28 16:40:02 UTC
Permalink
Hi Ciara,
Post by Ciara Power
This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.
On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.
---
lib/librte_telemetry/rte_telemetry.c | 205 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
2 files changed, 209 insertions(+)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 8d7b0e3..f984929 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,21 +3,159 @@
*/
#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
+#include <rte_string_fns.h>
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#define SLEEP_TIME 10
+#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+
+const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;
+int32_t
+rte_telemetry_check_port_activity(int port_id)
+{
+ int pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ if (pid == port_id)
+ return 1;
+ }
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid, not active\n",
+ port_id);
+ return 0;
+}
+
This function seems more about ethdev than telemetry.
Maybe add it as part of librte_ethdev.

The "active" semantic is blurry however. With this implementation, this
is checking that the device is currently attached and owned by the
default user. Should telemetry be limited to devices owned by default
user?

Also, this function does not seem used in this patch, can it be added
when used?
Post by Ciara Power
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
"_to" might not be necessary.
Post by Ciara Power
+{
+ int ret, num_xstats, start_index, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid\n", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) failed:"
+ " %d\n", port_id, num_xstats);
I guess there isn't really a consensus yet, as the checkpatch.sh tool
might be misconfigured, but the cesura is very awkward here.

Same for other logs.
Post by Ciara Power
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for"
+ " xstats\n");
+ return -ENOMEM;
+ }
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) len%i"
+ " failed: %d\n", port_id, num_xstats, ret);
+ return -EPERM;
+ }
+ struct rte_eth_xstat_name *eth_xstats_names;
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) *
+ num_xstats);
+ if (eth_xstats_names == NULL) {
You are sometimes checking pointers against NULL, sometimes using "!".
You can choose either in your library, but it would be better to be
consistent and use a unified coding style.
Post by Ciara Power
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for"
+ " xstats_names\n");
+ return -ENOMEM;
+ }
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names,
+ num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ free(eth_xstats_names);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get_names(%u)"
+ " len%i failed: %d\n", port_id, num_xstats,
+ ret);
+ return -EPERM;
+ }
+ const char *xstats_names[num_xstats];
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ start_index = rte_metrics_reg_names(xstats_names, num_xstats);
+
+ if (start_index < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_metrics_reg_names failed -"
+ " metrics may already be registered\n");
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return -1;
+ }
+ free(eth_xstats_names);
+ free(eth_xstats);
At this point, you have repeated 3 times those frees().
It would be cleaner to define proper labels and use goto instead.

[snip]
--
Gaëtan Rivet
6WIND
Van Haaren, Harry
2018-08-28 17:03:55 UTC
Permalink
Sent: Tuesday, August 28, 2018 5:40 PM
Subject: Re: [dpdk-dev] [PATCH 02/11] telemetry: add initial connection
socket
<snip>
Post by Ciara Power
+int32_t
+rte_telemetry_check_port_activity(int port_id)
+{
+ int pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ if (pid == port_id)
+ return 1;
+ }
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid, not active\n",
+ port_id);
+ return 0;
+}
+
This function seems more about ethdev than telemetry.
Maybe add it as part of librte_ethdev.
Yep that might be a better place, making it a generic ethdev function.
The "active" semantic is blurry however. With this implementation, this
is checking that the device is currently attached and owned by the
default user. Should telemetry be limited to devices owned by default
user?
Good question - perhaps one that we can get input on from others.
Would you (app X) want App Y to read telemetry from your ethdev/cryptodev?

Perhaps keeping telemetry to an "owned" port is a feature, perhaps
a limitation.

I'd like input on this one :D
Also, this function does not seem used in this patch, can it be added
when used?
Sure.
Post by Ciara Power
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
"_to" might not be necessary.
Post by Ciara Power
+{
+ int ret, num_xstats, start_index, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid\n", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) failed:"
+ " %d\n", port_id, num_xstats);
I guess there isn't really a consensus yet, as the checkpatch.sh tool
might be misconfigured, but the cesura is very awkward here.
Same for other logs.
Agreed, improvements possible here.
Post by Ciara Power
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for"
+ " xstats\n");
+ return -ENOMEM;
+ }
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) len%i"
+ " failed: %d\n", port_id, num_xstats, ret);
+ return -EPERM;
+ }
+ struct rte_eth_xstat_name *eth_xstats_names;
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) *
+ num_xstats);
+ if (eth_xstats_names == NULL) {
You are sometimes checking pointers against NULL, sometimes using "!".
You can choose either in your library, but it would be better to be
consistent and use a unified coding style.
Heh, I guess that's down to dev-style, and you'll note multiple sign-offs ;)

Can review for v2.
Post by Ciara Power
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for"
+ " xstats_names\n");
+ return -ENOMEM;
+ }
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names,
+ num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ free(eth_xstats_names);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get_names(%u)"
+ " len%i failed: %d\n", port_id, num_xstats,
+ ret);
+ return -EPERM;
+ }
+ const char *xstats_names[num_xstats];
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ start_index = rte_metrics_reg_names(xstats_names, num_xstats);
+
+ if (start_index < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_metrics_reg_names failed -"
+ " metrics may already be registered\n");
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return -1;
+ }
+ free(eth_xstats_names);
+ free(eth_xstats);
At this point, you have repeated 3 times those frees().
It would be cleaner to define proper labels and use goto instead.
Agreed.

Thanks for review!
Burakov, Anatoly
2018-09-07 09:48:19 UTC
Permalink
Post by Ciara Power
This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.
On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.
---
Quick question: is there really a need for a separate thread? Can't you
just register the socket as an interrupt, and just get a callback when
something arrives? IPC works like that right now, i see no reason to not
do it this way for telemetry?
--
Thanks,
Anatoly
Ciara Power
2018-08-23 12:08:05 UTC
Permalink
This patch introduces clients to the telemetry API.

When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.

A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.

Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
lib/librte_telemetry/meson.build | 2 +
lib/librte_telemetry/rte_telemetry.c | 358 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 25 ++
mk/rte.app.mk | 2 +-
4 files changed, 386 insertions(+), 1 deletion(-)

diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076..0ccfa36 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_telemetry.c')
headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
+jansson = cc.find_library('jansson', required: true)
+ext_deps += jansson
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index f984929..e9dd022 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -6,6 +6,7 @@
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <jansson.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
@@ -15,6 +16,8 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

+#define BUF_SIZE 1024
+#define ACTION_POST 1
#define SLEEP_TIME 10

#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
@@ -36,6 +39,88 @@ rte_telemetry_check_port_activity(int port_id)
return 0;
}

+int32_t
+rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
+ const char *json_string)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error, could not initialise "
+ "TELEMETRY_API\n");
+ return -1;
+ }
+ if (!telemetry->request_client) {
+ TELEMETRY_LOG_ERR("Error - No client has been chosen to"
+ " write to\n");
+ return -1;
+ }
+ if (!json_string) {
+ TELEMETRY_LOG_ERR("Error - Invalid JSON string!\n");
+ return -1;
+ }
+ ret = send(telemetry->request_client->fd,
+ json_string, strlen(json_string), 0);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Failed to write to socket for "
+ "client: %s\n", 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("Error - invalid error type\n");
+ return -EINVAL;
+ }
+
+ root = json_object();
+
+ if (!root) {
+ TELEMETRY_LOG_ERR("Error - Could not create root JSON "
+ "object\n");
+ return -EPERM;
+ }
+
+ ret = json_object_set_new(root, "status_code",
+ json_string(status_code));
+
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Status code field cannot be set\n");
+ return -EPERM;
+ }
+
+ ret = json_object_set_new(root, "data", json_null());
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Data field cannot be set\n");
+ return -EPERM;
+ }
+
+ json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_decref(root);
+
+ ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not write to socket\n");
+ return -EPERM;
+ }
+ return 0;
+}
+
static int32_t
rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
{
@@ -127,6 +212,31 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+ char buf[BUF_SIZE];
+ int buffer_read = 0;
+ errno = 0;
+
+ buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+ buf[buffer_read] = '\0';
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Error - Read error\n");
+ return -1;
+ } else if (buffer_read == 0) {
+ close(telemetry->accept_fd);
+ telemetry->accept_fd = 0;
+ } else {
+ int ret = rte_telemetry_parse_client_message(telemetry, buf);
+ if (ret < 0)
+ TELEMETRY_LOG_WARN("Warning - parse message failed\n");
+ close(telemetry->accept_fd);
+ telemetry->accept_fd = 0;
+ }
+ return 0;
+}
+
+static int32_t
rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
{
int ret;
@@ -148,6 +258,28 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
return -1;
}
}
+ } else {
+ ret = rte_telemetry_read_client(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - failed to read socket "
+ "buffer\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int32_t
+rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
+{
+ telemetry_client *client;
+ TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
+ char client_buf[BUF_SIZE];
+ int bytes = read(client->fd, client_buf, BUF_SIZE-1);
+ client_buf[bytes] = '\0';
+ if (bytes > 0) {
+ telemetry->request_client = client;
+ }
}
return 0;
}
@@ -169,6 +301,12 @@ rte_telemetry_run(void *userdata)
"failed\n");
return -1;
}
+
+ ret = rte_telemetry_read_client_sockets(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - client socket read failed\n");
+ return -1;
+ }
return 0;
}

@@ -267,6 +405,7 @@ rte_telemetry_init(uint32_t socket_id)
TELEMETRY_LOG_ERR("Error - TELEMETRY cleanup failed\n");
return -EPERM;
}
+ TAILQ_INIT(&static_telemetry->client_list_head);

pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
@@ -282,12 +421,38 @@ rte_telemetry_init(uint32_t socket_id)
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("Error - Close client socket failed\n");
+ 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("Error - Client cleanup failed\n");
+ return -EPERM;
+ }
+ }
ret = close(telemetry->server_fd);
if (ret < 0) {
TELEMETRY_LOG_ERR("Error - Close TELEMETRY socket failed\n");
@@ -301,6 +466,199 @@ rte_telemetry_cleanup(void)
return 0;
}

+int32_t
+rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
+ const char *client_path)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_WARN("Warning - TELEMETRY is not initialised\n");
+ return -ENODEV;
+ }
+
+ if (!client_path) {
+ TELEMETRY_LOG_ERR("Error - Invalid client path\n");
+ goto einval_fail;
+ }
+
+ if (TAILQ_EMPTY(&telemetry->client_list_head)) {
+ TELEMETRY_LOG_ERR("Error - there are no clients currently "
+ "registered\n");
+ return -EPERM;
+ }
+ telemetry_client *client, *temp_client;
+ 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("Error - Client cleanup "
+ "failed\n");
+ return -EPERM;
+ }
+ return 0;
+ }
+ }
+ TELEMETRY_LOG_WARN("Warning - couldn't find client, possibly not "
+ "registered yet.\n");
+ return -1;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -EINVAL;
+}
+
+int32_t
+rte_telemetry_register_client(struct telemetry_impl *telemetry,
+ const char *client_path)
+{
+ int ret, fd;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - could not initialize "
+ "TELEMETRY API\n");
+ return -ENODEV;
+ }
+
+ if (!client_path) {
+ TELEMETRY_LOG_ERR("Error - Invalid client path\n");
+ 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("Warning - '%s' already "
+ "registered\n", client_path);
+ return -EINVAL;
+ }
+ }
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ TELEMETRY_LOG_ERR("Error - Client socket error\n");
+ return -EACCES;
+ }
+ ret = rte_telemetry_set_socket_nonblock(fd);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not set socket to NONBLOCK\n");
+ return -EPERM;
+ }
+
+ struct sockaddr_un addrs = {0};
+ addrs.sun_family = AF_UNIX;
+ strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+ telemetry_client *new_client =
+ (telemetry_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("Error - TELEMETRY client connect to %s "
+ "didn't work\n", client_path);
+ ret = rte_telemetry_client_cleanup(new_client);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Client cleanup failed\n");
+ 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;
+ json_error_t error;
+ json_t *root = json_loads(buf, 0, &error);
+
+ if (!root) {
+ TELEMETRY_LOG_WARN("Warning - Could not load JSON object from "
+ "data passed in : %s\n", error.text);
+ goto fail;
+ } else if (!json_is_object(root)) {
+ TELEMETRY_LOG_WARN("Warning - JSON Request is not a JSON "
+ "object\n");
+ json_decref(root);
+ goto fail;
+ }
+
+ json_t *action = json_object_get(root, "action");
+ if (!action) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have action "
+ "field\n");
+ goto fail;
+ } else if (!json_is_integer(action)) {
+ TELEMETRY_LOG_WARN("Warning - Action value is not an "
+ "integer\n");
+ goto fail;
+ }
+
+ json_t *command = json_object_get(root, "command");
+ if (!command) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have command "
+ "field\n");
+ goto fail;
+ } else if (!json_is_string(command)) {
+ TELEMETRY_LOG_WARN("Warning - Command value is not a string\n");
+ goto fail;
+ }
+
+ int action_int = json_integer_value(action);
+ if (action_int != ACTION_POST) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action code\n");
+ goto fail;
+ }
+
+ const char *command_string = json_string_value(command);
+ if (strcmp(command_string, "clients") != 0) {
+ TELEMETRY_LOG_WARN("Warning - Invalid command\n");
+ goto fail;
+ }
+
+ json_t *data = json_object_get(root, "data");
+ if (!data) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have data "
+ "field\n");
+ goto fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
+ if (!client_path) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have "
+ "client_path field\n");
+ goto fail;
+ }
+ if (!json_is_string(client_path)) {
+ TELEMETRY_LOG_WARN("Warning - client_path value is not a "
+ "string\n");
+ goto fail;
+ }
+
+ const char *client_path_string = json_string_value(client_path);
+
+ ret = rte_telemetry_register_client(telemetry, client_path_string);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - could not register client\n");
+ telemetry->register_fail_count++;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ TELEMETRY_LOG_WARN("Warning - Client attempted to register with "
+ "invalid message\n");
+ return -1;
+}
+
int telemetry_log_level;
RTE_INIT(rte_telemetry_log_init);

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 569d56a..e3292cf 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -3,6 +3,7 @@
*/

#include <rte_log.h>
+#include <rte_tailq.h>

#ifndef _RTE_TELEMETRY_INTERNAL_H_
#define _RTE_TELEMETRY_INTERNAL_H_
@@ -23,6 +24,12 @@ extern int telemetry_log_level;
#define TELEMETRY_LOG_INFO(fmt, args...) \
TELEMETRY_LOG(INFO, fmt, ## args)

+typedef struct telemetry_client {
+ char *file_path;
+ int fd;
+ TAILQ_ENTRY(telemetry_client) client_list;
+} telemetry_client;
+
typedef struct telemetry_impl {
int accept_fd;
int server_fd;
@@ -31,6 +38,24 @@ typedef struct telemetry_impl {
uint32_t socket_id;
int reg_index;
int metrics_register_done;
+ TAILQ_HEAD(, telemetry_client) client_list_head;
+ struct telemetry_client *request_client;
+ int register_fail_count;
} telemetry_impl;

+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);
+
+int32_t
+rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
+ int error_type);
+
+int32_t
+rte_telemetry_register_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
+int32_t
+rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
#endif
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 8551191..1963812 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
-_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry -ljansson
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.7.4
Stephen Hemminger
2018-08-23 23:27:33 UTC
Permalink
On Thu, 23 Aug 2018 13:08:05 +0100
Post by Ciara Power
This patch introduces clients to the telemetry API.
When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.
A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.
Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.
Rather than using the rather heavyweight jansson library and creating
an additional dependency on an external library; may I recommend reusing
the json_writer library (I wrote) that is part of iproute2 and much
simpler.

https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/json_writer.c
Hunt, David
2018-08-28 15:26:38 UTC
Permalink
Post by Stephen Hemminger
On Thu, 23 Aug 2018 13:08:05 +0100
Post by Ciara Power
This patch introduces clients to the telemetry API.
When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.
A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.
Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.
Rather than using the rather heavyweight jansson library and creating
an additional dependency on an external library; may I recommend reusing
the json_writer library (I wrote) that is part of iproute2 and much
simpler.
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/json_writer.c
Hi Stephen, Ciara,

I'm about to push another patchset to the mailing list in the next few
days which
also makes use of Jansson. I'm parsing an incoming JSON string
containing power
management info. The Jansson package comes pre-installed in many
operating systems,
although you do indeed need to install libjansson-dev to build against it.
I would certainly like to see the community accept its use.

Regards,
Dave.
Van Haaren, Harry
2018-08-28 17:09:37 UTC
Permalink
-----Original Message-----
From: Hunt, David
Sent: Tuesday, August 28, 2018 4:27 PM
Subject: Re: [dpdk-dev] [PATCH 03/11] telemetry: add client feature and
sockets
Post by Stephen Hemminger
On Thu, 23 Aug 2018 13:08:05 +0100
Post by Ciara Power
This patch introduces clients to the telemetry API.
When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.
A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.
Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.
Rather than using the rather heavyweight jansson library and creating
an additional dependency on an external library; may I recommend reusing
the json_writer library (I wrote) that is part of iproute2 and much
simpler.
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/json_w
riter.c
Although I tend to prefer lightweight or dependency-free software, in this
case I think the requirements (parsing and writing JSON) is more than we
should implement our own version of in DPDK.

Jansson provides all we need, and as Dave notes below it is a common
library with good packaging support already.

With the Meson build system, I'd like to see it being picked up dynamically
and compiling in automatically when available.

For the existing Make system, I'm OK with Telemetry being off by default
and users switching it on if they wish to accept Jansson dep and gain the
telemetry functionality.
I'm about to push another patchset to the mailing list in the next few
days which
also makes use of Jansson. I'm parsing an incoming JSON string
containing power
management info. The Jansson package comes pre-installed in many
operating systems,
although you do indeed need to install libjansson-dev to build against it.
I would certainly like to see the community accept its use.
Ciara Power
2018-08-23 12:08:06 UTC
Permalink
This patch adds the parser file. This is used to parse any
messages that are received on any of the client sockets.

Currently, the unregister functionality works using the parser.
Functionality relating to getting statistic values for certain ports
will be added in a subsequent patch, however the parsing involved
for that command is added in this patch.

Some of the parser code included is in preparation for future
functionality, that is not implemented yet in this patchset.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 9 +
lib/librte_telemetry/rte_telemetry_internal.h | 3 +
lib/librte_telemetry/rte_telemetry_parser.c | 585 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
6 files changed, 613 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index bda3788..df8fdd9 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -19,6 +19,7 @@ LIBABIVER := 1

# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 0ccfa36..7450f96 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index e9dd022..c6c6612 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -15,6 +15,7 @@

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
+#include "rte_telemetry_parser.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
@@ -272,6 +273,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
static int32_t
rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
{
+ int ret;
+
telemetry_client *client;
TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
char client_buf[BUF_SIZE];
@@ -279,6 +282,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
client_buf[bytes] = '\0';
if (bytes > 0) {
telemetry->request_client = client;
+ ret = rte_telemetry_parse(telemetry, client_buf);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Parse socket "
+ "input failed: %i\n", ret);
+ return -1;
+ }
}
}
return 0;
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf..b057794 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -58,4 +58,7 @@ int32_t
rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
const char *client_path);

+int32_t
+rte_telemetry_check_port_activity(int port_id);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
new file mode 100644
index 0000000..571c991
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -0,0 +1,585 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+
+#include <rte_metrics.h>
+#include <rte_common.h>
+#include <rte_ethdev.h>
+
+#include "rte_telemetry_internal.h"
+
+#define ACTION_GET 0
+#define ACTION_DELETE 2
+
+struct command {
+ char *command_text;
+ int (*comm_func_ptr)(struct telemetry_impl *, int, json_t *);
+} command;
+
+static int32_t
+rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
+ if (!json_is_string(client_path)) {
+ TELEMETRY_LOG_WARN("Warning - Command value is not a string\n");
+ goto einval_fail;
+ }
+
+ const char *client_path_string = json_string_value(client_path);
+
+ ret = rte_telemetry_unregister_client(telemetry, client_path_string);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - could not unregister client\n");
+ goto einval_fail;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+static int32_t
+rte_telemetry_command_ports(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Warning - Data should be NULL JSON object "
+ "for 'ports' command\n");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+static int32_t
+rte_telemetry_command_ports_details(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ json_t *port_ids_json = json_object_get(data, "ports");
+ if (!json_is_array(port_ids_json)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid Port ID array\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ int port_ids[num_port_ids];
+ RTE_SET_USED(port_ids);
+ size_t index;
+ json_t *value;
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Warning - Port ID given is "
+ "invalid\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ }
+
+ return 0;
+}
+
+static int32_t
+rte_telemetry_command_port_stats(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Warning - Data should be NULL JSON object "
+ "for 'port_stats' command\n");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+static int32_t
+rte_telemetry_stat_names_to_ids(struct telemetry_impl *telemetry,
+ const char * const *stat_names, uint32_t *stat_ids,
+ uint64_t num_stat_names)
+{
+ struct rte_metric_name *names;
+ int ret;
+
+ if (stat_names == NULL) {
+ TELEMETRY_LOG_WARN("Warning - Invalid stat_names argument\n");
+ goto einval_fail;
+ }
+
+ if (num_stat_names <= 0) {
+ TELEMETRY_LOG_WARN("Warning - Invalid num_stat_names "
+ "argument\n");
+ goto einval_fail;
+ }
+
+ int num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics count\n");
+ goto eperm_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_WARN("Warning - No metrics have been "
+ "registered\n");
+ goto eperm_fail;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Error - Cannot allocate memory for names\n");
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics names\n");
+ free(names);
+ goto eperm_fail;
+ }
+
+ uint32_t i, k;
+ k = 0;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ uint32_t j;
+ for (j = 0; j < (uint32_t)num_metrics; j++) {
+ if (strcmp(stat_names[i], names[j].name) == 0) {
+ stat_ids[k] = j;
+ k++;
+ break;
+ }
+ }
+ }
+
+ if (k != num_stat_names) {
+ TELEMETRY_LOG_WARN("Warning - Invalid stat names provided\n");
+ free(names);
+ goto einval_fail;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret, num_metrics;
+ struct rte_metric_name *names;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ if (json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics count\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("Error - No metrics to display (none have been registered)\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (!names) {
+ TELEMETRY_LOG_ERR("Error - Cannot allocate memory\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ uint64_t num_port_ids = 0;
+ const char *stat_names[num_metrics];
+ uint32_t stat_ids[num_metrics];
+ int p;
+
+ RTE_ETH_FOREACH_DEV(p) {
+ num_port_ids++;
+ }
+ if (!num_port_ids) {
+ TELEMETRY_LOG_WARN("Warning - No active ports\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ goto fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ int i;
+ for (i = 0; i < num_metrics; i++)
+ stat_names[i] = names[i].name;
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_metrics);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not convert stat names to IDs\n");
+ goto fail;
+ }
+ return 0;
+
+fail:
+ free(names);
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
+ *telemetry, int action, json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ json_t *port_ids_json = json_object_get(data, "ports");
+ json_t *stat_names_json = json_object_get(data, "stats");
+ if (!json_is_array(port_ids_json) ||
+ !json_is_array(stat_names_json)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid input data array(s)\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ uint32_t port_ids[num_port_ids];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Warning - Port ID given is not "
+ "valid\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ ret = rte_telemetry_check_port_activity(port_ids[index]);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ }
+
+ uint64_t num_stat_names = json_array_size(stat_names_json);
+ const char *stat_names[num_stat_names];
+
+ json_array_foreach(stat_names_json, index, value) {
+ if (!json_is_string(value)) {
+ TELEMETRY_LOG_WARN("Warning - Stat Name given is not a "
+ "string\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ stat_names[index] = json_string_value(value);
+ }
+
+ uint32_t stat_ids[num_stat_names];
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_stat_names);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not convert stat names to "
+ "IDs\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int32_t
+rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
+ const char *command, json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ struct command commands[] = {
+ {.command_text = "clients",
+ .comm_func_ptr = &rte_telemetry_command_clients},
+ {.command_text = "ports",
+ .comm_func_ptr = &rte_telemetry_command_ports},
+ {.command_text = "ports_details",
+ .comm_func_ptr = &rte_telemetry_command_ports_details},
+ {.command_text = "port_stats",
+ .comm_func_ptr = &rte_telemetry_command_port_stats},
+ {.command_text = "ports_stats_values_by_name",
+ .comm_func_ptr =
+ &rte_telemetry_command_ports_stats_values_by_name},
+ {.command_text = "ports_all_stat_values",
+ .comm_func_ptr =
+ &rte_telemetry_command_ports_all_stat_values}
+ };
+
+ const uint32_t num_commands = sizeof(commands)/sizeof(struct command);
+ uint32_t i;
+
+ for (i = 0; i < num_commands; i++) {
+ if (strcmp(command, commands[i].command_text) == 0) {
+ int ret = commands[i].comm_func_ptr(telemetry, action,
+ data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Command Function "
+ "for %s failed\n",
+ commands[i].command_text);
+ return -1;
+ }
+ return 0;
+ }
+ }
+ TELEMETRY_LOG_WARN("Warning - \"%s\" command not found\n", command);
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ json_error_t error;
+ json_t *root = json_loads(socket_rx_data, 0, &error);
+ if (!root) {
+ TELEMETRY_LOG_WARN("Warning - Could not load JSON object from "
+ "data passed in : %s\n", error.text);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -EPERM;
+ } else if (!json_is_object(root)) {
+ TELEMETRY_LOG_WARN("Warning - JSON Request is not a JSON "
+ "object\n");
+ json_decref(root);
+ goto einval_fail;
+ }
+
+ json_t *action = json_object_get(root, "action");
+ if (!action) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have action "
+ "field\n");
+ goto einval_fail;
+ } else if (!json_is_integer(action)) {
+ TELEMETRY_LOG_WARN("Warning - Action value is not an "
+ "integer\n");
+ goto einval_fail;
+ }
+
+ json_t *command = json_object_get(root, "command");
+ if (!command) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have command "
+ "field\n");
+ goto einval_fail;
+ } else if (!json_is_string(command)) {
+ TELEMETRY_LOG_WARN("Warning - Command value is not a string\n");
+ goto einval_fail;
+ }
+
+ int action_int = json_integer_value(action);
+ if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action code\n");
+ goto einval_fail;
+ }
+
+ const char *command_string = json_string_value(command);
+ json_t *data = json_object_get(root, "data");
+ if (!data) {
+ TELEMETRY_LOG_WARN("Warning - Request does not have data "
+ "field\n");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
+ data);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Could not parse command\n");
+ return -EINVAL;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -EPERM;
+ }
+ return -EINVAL;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser.h b/lib/librte_telemetry/rte_telemetry_parser.h
new file mode 100644
index 0000000..63e633d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_PARSER_H_
+#define _RTE_TELEMETRY_PARSER_H_
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data);
+
+#endif
--
2.7.4
Gaëtan Rivet
2018-08-30 23:57:43 UTC
Permalink
Hi,
Post by Ciara Power
This patch adds the parser file. This is used to parse any
messages that are received on any of the client sockets.
Currently, the unregister functionality works using the parser.
Functionality relating to getting statistic values for certain ports
will be added in a subsequent patch, however the parsing involved
for that command is added in this patch.
Some of the parser code included is in preparation for future
functionality, that is not implemented yet in this patchset.
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 9 +
lib/librte_telemetry/rte_telemetry_internal.h | 3 +
lib/librte_telemetry/rte_telemetry_parser.c | 585 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
6 files changed, 613 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index bda3788..df8fdd9 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -19,6 +19,7 @@ LIBABIVER := 1
# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c
# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 0ccfa36..7450f96 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation
-sources = files('rte_telemetry.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index e9dd022..c6c6612 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -15,6 +15,7 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
+#include "rte_telemetry_parser.h"
#define BUF_SIZE 1024
#define ACTION_POST 1
@@ -272,6 +273,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
static int32_t
rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
{
+ int ret;
+
telemetry_client *client;
TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
char client_buf[BUF_SIZE];
@@ -279,6 +282,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
client_buf[bytes] = '\0';
if (bytes > 0) {
telemetry->request_client = client;
+ ret = rte_telemetry_parse(telemetry, client_buf);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Parse socket "
+ "input failed: %i\n", ret);
I see LOG_WARN being always preceded by "Warning - ",
LOG_ERR by "Error - ", and so on.

Wouldn't it be simpler to have the prefix inserted systematically? I
have seen at least one mistake on a LOG_ERR message (in another patch).

Also Shreyansh already remarked about it, but you may have doubled the
newline.
Post by Ciara Power
+ return -1;
+ }
}
}
return 0;
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf..b057794 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -58,4 +58,7 @@ int32_t
rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
const char *client_path);
+int32_t
+rte_telemetry_check_port_activity(int port_id);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
new file mode 100644
index 0000000..571c991
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -0,0 +1,585 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+
+#include <rte_metrics.h>
+#include <rte_common.h>
+#include <rte_ethdev.h>
+
+#include "rte_telemetry_internal.h"
+
+#define ACTION_GET 0
+#define ACTION_DELETE 2
An enum would be cleaner here, I don't see a reason for
these values to be defines.
Post by Ciara Power
+
+struct command {
struct rte_telemetry_command might be a better name.
Post by Ciara Power
+ char *command_text;
command.command_text? Why not simply text?
Post by Ciara Power
+ int (*comm_func_ptr)(struct telemetry_impl *, int, json_t *);
Function pointers should be typedef for readability.
Post by Ciara Power
+} command;
+
+static int32_t
+rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
blank line missing here.
However, it seems ret is never actually used.
All checks could be performed against the function calls directly,
as the return value is never used itself, -1 is always returned.
Post by Ciara Power
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
Coding style guide dictates local variables to be defined at start of
scope.

[※]: https://doc.dpdk.org/guides/contributing/coding_style.html#local-variables
Post by Ciara Power
+ if (!json_is_string(client_path)) {
+ TELEMETRY_LOG_WARN("Warning - Command value is not a string\n");
+ goto einval_fail;
+ }
+
+ const char *client_path_string = json_string_value(client_path);
This variable is not used afterward, why not write:

if (rte_telemetry_unregister_client(telemetry,
json_string_value(client_path))) {
TELEMETRY_LOG_ERR("Could not unregister client");
goto einval_fail;
}
Post by Ciara Power
+
+ ret = rte_telemetry_unregister_client(telemetry, client_path_string);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - could not unregister client\n");
+ goto einval_fail;
+ }
+
+ return 0;
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+static int32_t
+rte_telemetry_command_ports(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Warning - Data should be NULL JSON object "
+ "for 'ports' command\n");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ goto einval_fail;
+ }
+
+ return 0;
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+static int32_t
+rte_telemetry_command_ports_details(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
Previous functions used gotos for dealing with errors, this one repeats
the pattern. Seems like an oversight.
Post by Ciara Power
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ json_t *port_ids_json = json_object_get(data, "ports");
+ if (!json_is_array(port_ids_json)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid Port ID array\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ int port_ids[num_port_ids];
+ RTE_SET_USED(port_ids);
+ size_t index;
+ json_t *value;
Declare those at start of scope.
Post by Ciara Power
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Warning - Port ID given is "
+ "invalid\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ }
+
+ return 0;
+}
+
Some previous errors are repeated afterward but I won't point each of
them.

[snip]
Post by Ciara Power
+
+static int32_t
+rte_telemetry_stat_names_to_ids(struct telemetry_impl *telemetry,
+ const char * const *stat_names, uint32_t *stat_ids,
+ uint64_t num_stat_names)
+{
+ struct rte_metric_name *names;
+ int ret;
+
+ if (stat_names == NULL) {
+ TELEMETRY_LOG_WARN("Warning - Invalid stat_names argument\n");
+ goto einval_fail;
+ }
+
+ if (num_stat_names <= 0) {
+ TELEMETRY_LOG_WARN("Warning - Invalid num_stat_names "
+ "argument\n");
+ goto einval_fail;
+ }
+
+ int num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics count\n");
+ goto eperm_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_WARN("Warning - No metrics have been "
+ "registered\n");
+ goto eperm_fail;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Error - Cannot allocate memory for names\n");
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics names\n");
+ free(names);
+ goto eperm_fail;
+ }
+
+ uint32_t i, k;
+ k = 0;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ uint32_t j;
+ for (j = 0; j < (uint32_t)num_metrics; j++) {
+ if (strcmp(stat_names[i], names[j].name) == 0) {
+ stat_ids[k] = j;
+ k++;
+ break;
+ }
+ }
+ }
+
+ if (k != num_stat_names) {
+ TELEMETRY_LOG_WARN("Warning - Invalid stat names provided\n");
It would be better to provide the user with a list of requested names
that are invalid.
Post by Ciara Power
+ free(names);
+ goto einval_fail;
+ }
+
You are not freeing "names" here, nor are you returning it.
Post by Ciara Power
+ return 0;
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret, num_metrics;
+ struct rte_metric_name *names;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ if (json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics count\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("Error - No metrics to display (none have been registered)\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (!names) {
+ TELEMETRY_LOG_ERR("Error - Cannot allocate memory\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ uint64_t num_port_ids = 0;
+ const char *stat_names[num_metrics];
+ uint32_t stat_ids[num_metrics];
+ int p;
+
+ RTE_ETH_FOREACH_DEV(p) {
+ num_port_ids++;
+ }
+ if (!num_port_ids) {
+ TELEMETRY_LOG_WARN("Warning - No active ports\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ goto fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ int i;
+ for (i = 0; i < num_metrics; i++)
+ stat_names[i] = names[i].name;
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_metrics);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not convert stat names to IDs\n");
+ goto fail;
+ }
+ return 0;
The retrieved IDs here are not used, is it because this function is
extended in another patch?

You also already use rte_metrics_get_names() to generate a request, that
will itself do the same, compare the two and assign IDs according to
their index in the list.

This could probably be written in a simpler and more concise way.

This is rather awkward to divide the patches this way. It is harder
right now to judge this function and how stat_names_to_ids is
implemented, because the finality of it is not yet available.

It was not necessary to add this command alongside the others, it could
come later.
Post by Ciara Power
+
+ free(names);
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
+ *telemetry, int action, json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Warning - Invalid action for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid data provided for this "
+ "command\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ json_t *port_ids_json = json_object_get(data, "ports");
+ json_t *stat_names_json = json_object_get(data, "stats");
+ if (!json_is_array(port_ids_json) ||
+ !json_is_array(stat_names_json)) {
+ TELEMETRY_LOG_WARN("Warning - Invalid input data array(s)\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ uint32_t port_ids[num_port_ids];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Warning - Port ID given is not "
+ "valid\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ ret = rte_telemetry_check_port_activity(port_ids[index]);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ }
+
+ uint64_t num_stat_names = json_array_size(stat_names_json);
+ const char *stat_names[num_stat_names];
+
+ json_array_foreach(stat_names_json, index, value) {
+ if (!json_is_string(value)) {
+ TELEMETRY_LOG_WARN("Warning - Stat Name given is not a "
+ "string\n");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send "
+ "error\n");
+ return -1;
+ }
+ stat_names[index] = json_string_value(value);
+ }
+
+ uint32_t stat_ids[num_stat_names];
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_stat_names);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not convert stat names to "
+ "IDs\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int32_t
+rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
+ const char *command, json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+
+ struct command commands[] = {
+ {.command_text = "clients",
+ .comm_func_ptr = &rte_telemetry_command_clients},
+ {.command_text = "ports",
+ .comm_func_ptr = &rte_telemetry_command_ports},
+ {.command_text = "ports_details",
+ .comm_func_ptr = &rte_telemetry_command_ports_details},
+ {.command_text = "port_stats",
+ .comm_func_ptr = &rte_telemetry_command_port_stats},
+ {.command_text = "ports_stats_values_by_name",
+ .comm_func_ptr =
+ &rte_telemetry_command_ports_stats_values_by_name},
+ {.command_text = "ports_all_stat_values",
+ .comm_func_ptr =
+ &rte_telemetry_command_ports_all_stat_values}
These command names are unnecessarily verbose, while still not saying
exactly what the commands are.

"clients" only supports "DELETE", but the function name does not reflect
that. "ports" only supports "GET", but this is not explicit.
And on the other hand,
"rte_telemetry_command_ports_stats_values_by_name" is just too much.

The coding style is wrong as well.

With the proper definitions, something like this could be written:

struct rte_telemetry_command commands[] = {
{
.text = "client",
.fn = {
[DELETE] = rte_tlm_client_unregister,
},
},
{
.text = "port",
.fn = {
[GET] = rte_tlm_port_get,
},
},
...
};
Post by Ciara Power
+ };
+
+ const uint32_t num_commands = sizeof(commands)/sizeof(struct command);
RTE_DIM() should be used for this.
Post by Ciara Power
+ uint32_t i;
+
Here action should be checked against the enum previously described,
verifying that it is within range.
Post by Ciara Power
+ for (i = 0; i < num_commands; i++) {
+ if (strcmp(command, commands[i].command_text) == 0) {
+ int ret = commands[i].comm_func_ptr(telemetry, action,
+ data);
With the above format, this would become

if (strcmp(command, commands[i].text) == 0) {
rte_telemetry_command_cb fn;

fn = commands[i].fn[action];
if (fn == NULL) {
/* Error invalid command */
return -1;
} else if (fn(telemetry, data)) {
/* Error log would already be provided
* by the function itself.
*/
return -1;
}
return 0;
}

Regards,
--
Gaëtan Rivet
6WIND
Ciara Power
2018-08-23 12:08:07 UTC
Permalink
This patch adds functionality to update the statistics in
the metrics library with values from the ethdev stats.

Values need to be updated before they are encoded into a JSON
message and sent to the client that requested them. The JSON encoding
will be added in a subsequent patch.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 128 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
lib/librte_telemetry/rte_telemetry_parser.c | 19 ++++
3 files changed, 151 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index c6c6612..bd09374 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -40,6 +40,74 @@ rte_telemetry_check_port_activity(int port_id)
return 0;
}

+static int32_t
+rte_telemetry_update_metrics_ethdev(struct telemetry_impl *telemetry,
+ uint16_t port_id, int reg_start_index)
+{
+ int ret, num_xstats, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("Error - port_id: %d is invalid\n", port_id);
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+ ret = rte_telemetry_check_port_activity(port_id);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) failed:"
+ " %d\n", port_id, num_xstats);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Error - Failed to malloc memory for "
+ "xstats\n");
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("Error - rte_eth_xstats_get(%u) len%i"
+ " failed: %d\n", port_id, num_xstats, ret);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+ uint64_t xstats_values[num_xstats];
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_values[i] = eth_xstats[i].value;
+ ret = rte_metrics_update_values(port_id, reg_start_index,
+ xstats_values, num_xstats);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not update metrics values\n");
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ free(eth_xstats);
+ return -1;
+ }
+
+ free(eth_xstats);
+ return 0;
+}
+
int32_t
rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
const char *json_string)
@@ -122,6 +190,66 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
+{
+ int ret;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Invalid telemetry argument\n");
+ return -1;
+ }
+ if (metric_ids == NULL) {
+ TELEMETRY_LOG_ERR("Error - Invalid metric_ids array\n");
+ goto einval_fail;
+ }
+ if (num_metric_ids < 0) {
+ TELEMETRY_LOG_ERR("Error - Invalid num_metric_ids, must be "
+ "positive\n");
+ goto einval_fail;
+ }
+ if (port_ids == NULL) {
+ TELEMETRY_LOG_ERR("Error - Invalid port_ids array\n");
+ goto einval_fail;
+ }
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Error - Invalid num_port_ids, must be "
+ "positive\n");
+ goto einval_fail;
+ }
+
+ int i;
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Error - Port: %d invalid\n",
+ port_ids[i]);
+ goto einval_fail;
+ }
+ ret = rte_telemetry_update_metrics_ethdev(telemetry,
+ port_ids[i], telemetry->reg_index);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - failed to update ethdev "
+ "metrics\n");
+ return -1;
+ }
+ }
+
+ char *json_buffer = NULL;
+ ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not write to socket\n");
+ return -1;
+ }
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+
static int32_t
rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
{
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index b057794..ef417f2 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -61,4 +61,8 @@ rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
int32_t
rte_telemetry_check_port_activity(int port_id);

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
index 571c991..4c50e41 100644
--- a/lib/librte_telemetry/rte_telemetry_parser.c
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -327,8 +327,10 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
const char *stat_names[num_metrics];
uint32_t stat_ids[num_metrics];
int p;
+ uint32_t port_ids[RTE_MAX_ETHPORTS];

RTE_ETH_FOREACH_DEV(p) {
+ port_ids[num_port_ids] = p;
num_port_ids++;
}
if (!num_port_ids) {
@@ -351,6 +353,14 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
TELEMETRY_LOG_ERR("Error - Could not convert stat names to IDs\n");
goto fail;
}
+
+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_metrics,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Sending ports stats values failed\n");
+ goto fail;
+ }
+
return 0;

fail:
@@ -451,6 +461,15 @@ rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
"IDs\n");
return -1;
}
+
+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Sending ports stats values "
+ "failed\n");
+ return -1;
+ }
+
return 0;
}
--
2.7.4
Ciara Power
2018-08-23 12:08:08 UTC
Permalink
This patch adds functionality to create a JSON message in
order to send it to a client socket.

When stats are requested by a client, they are retrieved from
the metrics library and encoded in JSON format.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 314 ++++++++++++++++++++++++++++++++++-
1 file changed, 313 insertions(+), 1 deletion(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index bd09374..ba04d3d 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -179,7 +179,7 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return -EPERM;
}

- json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_buffer = json_dumps(root, 0);
json_decref(root);

ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
@@ -190,6 +190,311 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+static int
+rte_telemetry_get_metrics(struct telemetry_impl *telemetry, uint32_t port_id,
+ struct rte_metric_value *metrics, struct rte_metric_name *names,
+ int num_metrics)
+{
+ int ret, num_values;
+
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Error - Invalid metrics count\n");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been "
+ "registered)\n");
+ goto eperm_fail;
+ }
+
+ if (metrics == NULL) {
+ TELEMETRY_LOG_ERR("Error - metrics must be initialised.\n");
+ goto einval_fail;
+ }
+
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Error - names must be initialised.\n");
+ goto einval_fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics names\n");
+ goto eperm_fail;
+ }
+
+ num_values = rte_metrics_get_values(port_id, NULL, 0);
+ ret = rte_metrics_get_values(port_id, metrics, num_values);
+ if (ret < 0 || ret > num_values) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics values\n");
+ goto eperm_fail;
+ }
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+}
+
+static int32_t
+rte_telemetry_json_format_stat(struct telemetry_impl *telemetry, json_t *stats,
+ const char *metric_name, uint64_t metric_value)
+{
+ int ret;
+ json_t *stat = json_object();
+
+ if (!stat) {
+ TELEMETRY_LOG_ERR("Error - Could not create stat JSON "
+ "object\n");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "name", json_string(metric_name));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Stat Name field cannot be set\n");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "value", json_integer(metric_value));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Stat Value field cannot be set\n");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(stats, stat);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Stat cannot be added to stats json "
+ "array\n");
+ goto eperm_fail;
+ }
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+}
+
+static int32_t
+rte_telemetry_json_format_port(struct telemetry_impl *telemetry,
+ uint32_t port_id, json_t *ports, uint32_t *metric_ids,
+ uint32_t num_metric_ids)
+{
+ struct rte_metric_value *metrics = 0;
+ struct rte_metric_name *names = 0;
+ int num_metrics, ret;
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Error - Cannot get metrics count\n");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("Error - No metrics to display (none have "
+ "been registered)\n");
+ goto eperm_fail;
+ }
+
+ metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (metrics == NULL || names == NULL) {
+ TELEMETRY_LOG_ERR("Error - Cannot allocate memory\n");
+ free(metrics);
+ free(names);
+ int err_ret = rte_telemetry_send_error_response(telemetry,
+ -ENOMEM);
+ if (err_ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+ }
+
+ ret = rte_telemetry_get_metrics(telemetry, port_id, metrics, names,
+ num_metrics);
+ if (ret < 0) {
+ free(metrics);
+ free(names);
+ TELEMETRY_LOG_ERR("Error - rte_telemetry_get_metrics failed\n");
+ return -1;
+ }
+
+ json_t *port = json_object();
+ json_t *stats = json_array();
+ if (!port || !stats) {
+ TELEMETRY_LOG_ERR("Error - Could not create port/stats JSON "
+ "objects\n");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(port, "port", json_integer(port_id));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Port field cannot be set\n");
+ goto eperm_fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < num_metric_ids; i++) {
+ if (metric_ids[i] >= (uint32_t)num_metrics) {
+ TELEMETRY_LOG_ERR("Error - Metric_id: %d is "
+ "not valid\n", metric_ids[i]);
+ goto einval_fail;
+ }
+
+ int metric_id = metric_ids[i];
+ int metric_index = -1;
+ int metric_name_key = -1;
+ int32_t j;
+
+ for (j = 0; j < num_metrics; j++) {
+ if (metrics[j].key == metric_id) {
+ metric_name_key = metrics[j].key;
+ metric_index = j;
+ break;
+ }
+ }
+
+ if (metric_name_key < 0 || metric_index < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not get metric "
+ "name/index\n");
+ goto eperm_fail;
+ }
+
+ const char *metric_name = names[metric_name_key].name;
+ uint64_t metric_value = metrics[metric_index].value;
+ ret = rte_telemetry_json_format_stat(telemetry, stats,
+ metric_name, metric_value);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Format stat with id: %u "
+ "failed\n", metric_id);
+ free(metrics);
+ free(names);
+ return -1;
+ }
+ }
+
+ if (json_array_size(stats) == 0)
+ ret = json_object_set_new(port, "stats", json_null());
+ else
+ ret = json_object_set_new(port, "stats", stats);
+
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Stats object cannot be set\n");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(ports, port);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Port object cannot be added to "
+ "ports array\n");
+ goto eperm_fail;
+ }
+
+ free(metrics);
+ free(names);
+ return 0;
+
+eperm_fail:
+ free(metrics);
+ free(names);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+einval_fail:
+ free(metrics);
+ free(names);
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
+static int32_t
+rte_telemetry_encode_json_format(struct telemetry_impl *telemetry,
+ uint32_t *port_ids, uint32_t num_port_ids, uint32_t *metric_ids,
+ uint32_t num_metric_ids, char **json_buffer)
+{
+ int ret;
+ json_t *root, *ports;
+
+ if (num_port_ids <= 0 || num_metric_ids <= 0) {
+ TELEMETRY_LOG_ERR("Error - Please provide port and metric ids "
+ "to query\n");
+ goto einval_fail;
+ }
+
+ ports = json_array();
+ if (!ports) {
+ TELEMETRY_LOG_ERR("Error - Could not create ports JSON "
+ "array\n");
+ goto eperm_fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Error - Port: %d invalid\n",
+ port_ids[i]);
+ goto einval_fail;
+ }
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ ret = rte_telemetry_json_format_port(telemetry, port_ids[i],
+ ports, metric_ids, num_metric_ids);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - format port in JSON "
+ "failed\n");
+ return -1;
+ }
+ }
+
+ root = json_object();
+ if (!root) {
+ TELEMETRY_LOG_ERR("Error - Could not create root JSON "
+ "object\n");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "status_code",
+ json_string("Status OK: 200"));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Status code field cannot be set\n");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "data", ports);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Data field cannot be set\n");
+ goto eperm_fail;
+ }
+
+ *json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_decref(root);
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Error - Could not send error\n");
+ return -1;
+}
+
int32_t
rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
@@ -235,6 +540,13 @@ rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
}

char *json_buffer = NULL;
+ ret = rte_telemetry_encode_json_format(telemetry, port_ids,
+ num_port_ids, metric_ids, num_metric_ids, &json_buffer);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - JSON encode function failed\n");
+ return -1;
+ }
+
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Error - Could not write to socket\n");
--
2.7.4
Ciara Power
2018-08-23 12:08:09 UTC
Permalink
This patch adds all tests for the Telemetry API.
The tests added include a parser test, selftest, and socket
messaging tests.

The parser tests pass valid and invalid messages to the parser
to ensure the correct return values are received.
The selftest tests basic functions in the Telemetry API such as
registering, unregistering, and initialisation.
The socket messaging tests pass messages through the socket and
validates the return message, to ensure the Telemetry API is
responding correctly.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
drivers/telemetry/telemetry/telemetry_driver.c | 7 +
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 616 +++++++++++++++++++++-
lib/librte_telemetry/rte_telemetry.h | 12 +
lib/librte_telemetry/rte_telemetry_internal.h | 3 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 574 ++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 ++
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 ++
lib/librte_telemetry/rte_telemetry_version.map | 1 +
10 files changed, 1290 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h

diff --git a/drivers/telemetry/telemetry/telemetry_driver.c b/drivers/telemetry/telemetry/telemetry_driver.c
index c56f60c..125a89c 100644
--- a/drivers/telemetry/telemetry/telemetry_driver.c
+++ b/drivers/telemetry/telemetry/telemetry_driver.c
@@ -15,6 +15,13 @@ telemetry_probe(struct rte_vdev_device *vdev)
int ret;

RTE_SET_USED(vdev);
+ ret = rte_telemetry_selftest();
+ if (ret < 0) {
+ printf("Error - Selftest failed\n");
+ return -1;
+ }
+ printf("Success - Selftest passed\n");
+
ret = rte_telemetry_init(rte_socket_id());
if (ret < 0) {
printf("Error - Telemetry initialisation failed\n");
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index df8fdd9..d766c82 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -20,6 +20,7 @@ LIBABIVER := 1
# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser_test.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7450f96..57dd83d 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c', 'rte_telemetry_parser_test.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h', 'rte_telemetry_parser_test.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index ba04d3d..ecf644b 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -16,16 +16,34 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#include "rte_telemetry_parser.h"
+#include "rte_telemetry_parser_test.h"
+#include "rte_telemetry_socket_tests.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
#define SLEEP_TIME 10

#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+#define SELFTEST_VALID_CLIENT "/var/run/valid_client"
+#define SELFTEST_INVALID_CLIENT "/var/run/invalid_client"
+#define SOCKET_TEST_CLIENT_PATH "/var/run/client"

const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;

+struct telemetry_message_test {
+ char *test_name;
+ int (*test_func_ptr)(struct telemetry_impl *telemetry, int fd);
+};
+
+struct json_data {
+ char *status_code;
+ char *data;
+ int port;
+ char *stat_name;
+ int stat_value;
+};
+
int32_t
rte_telemetry_check_port_activity(int port_id)
{
@@ -634,7 +652,7 @@ rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
static int32_t
rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
- int pid;
+ int pid, ret;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index =
@@ -648,6 +666,17 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
return -1;
}
telemetry->metrics_register_done = 1;
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ telemetry->server_fd);
+ if (ret < 0)
+ return -1;
+
+ ret = rte_telemetry_parser_test(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Parser Tests Failed\n");
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - All Parser Tests Passed\n");

return 0;
}
@@ -1108,6 +1137,591 @@ rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
return -1;
}

+int32_t
+rte_telemetry_dummy_client_socket(const char *valid_client_path)
+{
+ int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ TELEMETRY_LOG_ERR("Error - Test socket creation failure\n");
+ return -1;
+ }
+ struct sockaddr_un addr = {0};
+ 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("Error - Test socket binding failure\n");
+ return -1;
+ }
+ if (listen(sockfd, 1) < 0) {
+ TELEMETRY_LOG_ERR("Error - Listen failure\n");
+ 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\n");
+
+ ret = rte_telemetry_init(rte_socket_id());
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Valid initialisation test"
+ " failed\n");
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Valid initialisation test passed\n");
+
+ ret = rte_telemetry_init(rte_socket_id());
+ if (ret != -EALREADY) {
+ TELEMETRY_LOG_ERR("Error - Invalid initialisation test "
+ "failed\n");
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Invalid initialisation test passed\n");
+
+ ret = rte_telemetry_unregister_client(static_telemetry,
+ invalid_client_path);
+ if (ret != -EPERM) {
+ TELEMETRY_LOG_ERR("Error - Invalid unregister test failed\n");
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Invalid unregister test passed\n");
+
+ sockfd = rte_telemetry_dummy_client_socket(valid_client_path);
+ if (sockfd < 0) {
+ TELEMETRY_LOG_ERR("Error - Test socket creation failed\n");
+ return -1;
+ }
+
+ ret = rte_telemetry_register_client(static_telemetry,
+ valid_client_path);
+ if (ret != 0) {
+ TELEMETRY_LOG_ERR("Error - Valid register test failed: %i\n",
+ ret);
+ return -1;
+ }
+ accept(sockfd, NULL, NULL);
+ TELEMETRY_LOG_INFO("Success - Valid register test passed\n");
+
+ ret = rte_telemetry_register_client(static_telemetry,
+ valid_client_path);
+ if (ret != -EINVAL) {
+ TELEMETRY_LOG_ERR("Error - Invalid register test failed: %i\n",
+ ret);
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Invalid register test passed\n");
+
+ ret = rte_telemetry_unregister_client(static_telemetry,
+ invalid_client_path);
+ if (ret != -1) {
+ TELEMETRY_LOG_ERR("Error - Invalid unregister test failed: "
+ "%i\n", ret);
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Invalid unregister test passed\n");
+
+ ret = rte_telemetry_unregister_client(static_telemetry,
+ valid_client_path);
+ if (ret != 0) {
+ TELEMETRY_LOG_ERR("Error - Valid unregister test failed: %i"
+ "\n", ret);
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Valid unregister test passed\n");
+
+ ret = rte_telemetry_cleanup();
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - cleanup() test failed\n");
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Valid cleanup test passed\n");
+
+ 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) {
+ TELEMETRY_LOG_ERR("Error - could not initialize "
+ "Telemetry API\n");
+ return -1;
+ }
+ telemetry->server_fd = socket;
+ telemetry->reg_index = index;
+ TELEMETRY_LOG_INFO("Beginning Telemetry socket message Selftest\n");
+ rte_telemetry_socket_test_setup(telemetry, &send_fd, &recv_fd);
+ TELEMETRY_LOG_INFO("Register valid client test\n");
+
+ ret = rte_telemetry_socket_register_test(telemetry, &fd, send_fd,
+ recv_fd);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Register valid client test "
+ "failed!\n");
+ free(telemetry);
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Register valid client test passed!\n");
+
+ TELEMETRY_LOG_INFO("Register invalid/same client test\n");
+ 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("Error - Register invalid/same client test "
+ "failed!\n");
+ free(telemetry);
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - Register invalid/same client test "
+ "passed!\n");
+
+ ret = rte_telemetry_json_socket_message_test(telemetry, fd);
+ if (ret < 0) {
+ free(telemetry);
+ return -1;
+ }
+
+ 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("Error - Could not send message over "
+ "socket\n");
+ 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;
+ *send_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ *recv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ listen(telemetry->server_fd, 5);
+ struct sockaddr_un addr = {0};
+ addr.sun_family = AF_UNIX;
+ 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("Error - could not connect socket\n");
+ return -1;
+ }
+ telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+
+ struct sockaddr_un addrs = {0};
+ 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("Error - could not bind socket\n");
+ 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("Error - JSON message is NULL\n");
+ return -EINVAL;
+ }
+
+ if (!root) {
+ TELEMETRY_LOG_ERR("Error - Could not load JSON object from "
+ "data passed in : %s\n", error.text);
+ return -EPERM;
+ } else if (!json_is_object(root)) {
+ TELEMETRY_LOG_ERR("Error - JSON Request is not a JSON "
+ "object\n");
+ json_decref(root);
+ return -EINVAL;
+ }
+
+ status = json_object_get(root, "status_code");
+ if (!status) {
+ TELEMETRY_LOG_ERR("Error - Request does not have status "
+ "field\n");
+ return -EINVAL;
+ } else if (!json_is_string(status)) {
+ TELEMETRY_LOG_ERR("Error - Status value is not a String\n");
+ return -EINVAL;
+ }
+
+ json_data_struct->status_code = strdup(json_string_value(status));
+
+ dataArray = json_object_get(root, "data");
+ if (!dataArray) {
+ TELEMETRY_LOG_ERR("Error - Request does not have data field\n");
+ 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) {
+ TELEMETRY_LOG_ERR("Error - Request does not have port field\n");
+ return -EINVAL;
+ }
+ if (!json_is_integer(port)) {
+ TELEMETRY_LOG_ERR("Error - Port value is not an integer\n");
+ return -EINVAL;
+ }
+
+ json_data_struct->port = json_integer_value(port);
+
+ if (!stats) {
+ TELEMETRY_LOG_ERR("Error - Request does not have stats "
+ "field\n");
+ 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) {
+ TELEMETRY_LOG_ERR("Error - Request does not have name field\n");
+ return -EINVAL;
+ }
+ if (!json_is_string(name)) {
+ TELEMETRY_LOG_ERR("Error - Stat name value is not a string\n");
+ return -EINVAL;
+ }
+ json_data_struct->stat_name = strdup(json_string_value(name));
+
+ if (!value) {
+ TELEMETRY_LOG_ERR("Error - Request does not have value "
+ "field\n");
+ return -EINVAL;
+ }
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_ERR("Error - Stat value is not an integer\n");
+ 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;
+ char buf[BUF_SIZE];
+ struct json_data *data_struct;
+ const char *status = "Status OK: 200";
+ int port = 0;
+ const char *name = "rx_good_packets";
+ int value = 0;
+ int fail_count = 0;
+ 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("Error - Could not send message over "
+ "socket\n");
+ return -1;
+ }
+ rte_telemetry_run(telemetry);
+ int buffer_read = 0;
+ errno = 0;
+ buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Error - Read error\n");
+ 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("Error - Could not parse stats\n");
+ fail_count++;
+ }
+
+ if (strcmp(data_struct->status_code, status) != 0) {
+ TELEMETRY_LOG_ERR(" Error - Status code is invalid\n");
+ fail_count++;
+ }
+ if (data_struct->port != port) {
+ TELEMETRY_LOG_ERR("Error - Port is invalid\n");
+ fail_count++;
+ }
+ if (strcmp(data_struct->stat_name, name) != 0) {
+ TELEMETRY_LOG_ERR("Error - Stat name is invalid\n");
+ fail_count++;
+ }
+ if (data_struct->stat_value != value) {
+ TELEMETRY_LOG_ERR("Error - Stat value is invalid\n");
+ 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\n");
+ 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("Error - Could not send message over "
+ "socket\n");
+ return -1;
+ }
+
+ rte_telemetry_run(telemetry);
+ buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Error - Read error\n");
+ 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("Error - Could not parse stats\n");
+
+ if (strcmp(data_struct->status_code, status) != 0) {
+ TELEMETRY_LOG_ERR("Error - Status code is invalid\n");
+ fail_count++;
+ }
+ if (strcmp(data_struct->data, data) != 0) {
+ TELEMETRY_LOG_ERR("Error - Data status is invalid\n");
+ fail_count++;
+ }
+ rte_telemetry_free_test_data(data_struct);
+ if (fail_count > 0)
+ return -1;
+
+ TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test\n");
+ 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;
+ char *status = "Status Error: Invalid Argument 404";
+ 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("Error - Could not send message over "
+ "socket\n");
+ return -1;
+ }
+ rte_telemetry_run(telemetry);
+ buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Error - Read error\n");
+ 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("Error - Could not parse stats\n");
+
+ if (strcmp(data_struct->status_code, status) != 0) {
+ TELEMETRY_LOG_ERR("Error - Status code is invalid\n");
+ fail_count++;
+ }
+ if (strcmp(data_struct->data, data) != 0) {
+ TELEMETRY_LOG_ERR("Error - Data status is invalid\n");
+ fail_count++;
+ }
+ rte_telemetry_free_test_data(data_struct);
+ if (fail_count > 0)
+ return -1;
+
+ TELEMETRY_LOG_INFO("Success - Passed invalid JSON content test\n");
+ 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";
+ 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("Error - Could not send message over "
+ "socket\n");
+ return -1;
+ }
+ rte_telemetry_run(telemetry);
+ buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Error - Read error\n");
+ 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("Error - Could not parse stats\n");
+
+ if (strcmp(data_struct->status_code, status) != 0) {
+ TELEMETRY_LOG_ERR("Error - Status code is invalid\n");
+ fail_count++;
+ }
+ if (strcmp(data_struct->data, data) != 0) {
+ TELEMETRY_LOG_ERR("Error - Data status is invalid\n");
+ fail_count++;
+ }
+ rte_telemetry_free_test_data(data_struct);
+ if (fail_count > 0)
+ return -1;
+
+ TELEMETRY_LOG_INFO("Success - Passed JSON empty message test\n");
+ 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 (sizeof(socket_json_tests)/sizeof(socket_json_tests[0]))
+
+ for (i = 0; i < NUM_TESTS; i++) {
+ TELEMETRY_LOG_INFO("%s\n",
+ socket_json_tests[i].test_name);
+ ret = (socket_json_tests[i].test_func_ptr)
+ (telemetry, fd);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - %s failed\n",
+ socket_json_tests[i].test_name);
+ fail_count++;
+ }
+ }
+ if (fail_count > 0) {
+ TELEMETRY_LOG_ERR("Error - Failed %i JSON socket message "
+ "test(s)", fail_count);
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - All JSON tests passed\n");
+ return 0;
+}
+
int telemetry_log_level;
RTE_INIT(rte_telemetry_log_init);

diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
index b691845..4f187b7 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -37,4 +37,16 @@ rte_telemetry_init(uint32_t socket_id);
int32_t
rte_telemetry_cleanup(void);

+/**
+ * Runs various tests to ensure telemetry initialisation and register/unregister
+ * functions are working correctly.
+ *
+ * @return
+ * 0 on success when all tests have passed
+ * @return
+ * -1 on failure when the test has failed
+ */
+int32_t
+rte_telemetry_selftest(void);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index ef417f2..3e21b79 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -65,4 +65,7 @@ int32_t
rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);

+int32_t
+rte_telemetry_socket_messaging_testing(int index, int socket);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.c b/lib/librte_telemetry/rte_telemetry_parser_test.c
new file mode 100644
index 0000000..4fa442d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.c
@@ -0,0 +1,574 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_tailq.h>
+#include <rte_string_fns.h>
+
+#include "rte_telemetry_parser.h"
+
+#define ACTION_GET 0
+#define ACTION_DELETE 2
+
+#define INV_ACTION_VAL 0
+#define INV_COMMAND_VAL 1
+#define INV_DATA_VAL 2
+#define INV_ACTION_FIELD 3
+#define INV_COMMAND_FIELD 4
+#define INV_DATA_FIELD 5
+#define INV_JSON_FORMAT 6
+#define VALID_REQ 7
+
+
+#define TEST_CLIENT "/var/run/test_client"
+
+int32_t
+rte_telemetry_create_test_socket(struct telemetry_impl *telemetry,
+ const char *test_client_path)
+{
+
+ int ret, sockfd;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Telemetry argument has not been "
+ "initialised\n");
+ return -EINVAL;
+ }
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ TELEMETRY_LOG_ERR("Error - Test socket creation failure\n");
+ return -1;
+ }
+
+ struct sockaddr_un addr = {0};
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, test_client_path, sizeof(addr.sun_path));
+ unlink(test_client_path);
+
+ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ TELEMETRY_LOG_ERR("Error - Test socket binding failure\n");
+ return -1;
+ }
+
+ if (listen(sockfd, 1) < 0) {
+ TELEMETRY_LOG_ERR("Error - Listen failure\n");
+ return -1;
+ }
+
+ ret = rte_telemetry_register_client(telemetry, test_client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Register dummy client failed: %i",
+ ret);
+ return -1;
+ }
+
+ ret = accept(sockfd, NULL, NULL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Socket accept failed\n");
+ return -1;
+ }
+
+ struct telemetry_client *client;
+ TAILQ_FOREACH(client, &telemetry->client_list_head, client_list)
+ telemetry->request_client = client;
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, json_t **data)
+{
+
+ int ret;
+ json_t *stat_names_json_array = NULL;
+ json_t *port_ids_json_array = NULL;
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Error - Port Ids Count invalid\n");
+ goto fail;
+ }
+
+ *data = json_object();
+ if (!*data) {
+ TELEMETRY_LOG_ERR("Error - Data json object creation failed\n");
+ goto fail;
+ }
+
+ port_ids_json_array = json_array();
+ if (!port_ids_json_array) {
+ TELEMETRY_LOG_ERR("Error - port_ids_json_array creation "
+ "failed\n");
+ goto fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < (uint32_t) num_port_ids; i++) {
+ ret = json_array_append(port_ids_json_array,
+ json_integer(port_ids[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - JSON array creation "
+ "failed\n");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "ports", port_ids_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting 'ports' value in data object"
+ " failed\n");
+ goto fail;
+ }
+
+ if (stat_names) {
+
+ if (num_stat_names < 0) {
+ TELEMETRY_LOG_ERR("Error - Stat Names Count invalid\n");
+ goto fail;
+ }
+
+ stat_names_json_array = json_array();
+ if (!stat_names_json_array) {
+ TELEMETRY_LOG_ERR("Error - stat_names_json_array "
+ "creation failed\n");
+ goto fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < (uint32_t) num_stat_names; i++) {
+ ret = json_array_append(stat_names_json_array,
+ json_string(stat_names[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - JSON array creation "
+ " failed\n");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "stats",
+ stat_names_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting 'stats' value in "
+ "data object failed\n");
+ goto fail;
+ }
+
+ }
+ return 0;
+
+fail:
+ if (*data)
+ json_decref(*data);
+ if (stat_names_json_array)
+ json_decref(stat_names_json_array);
+ if (port_ids_json_array)
+ json_decref(port_ids_json_array);
+ return -1;
+}
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, char **request,
+ int inv_choice)
+{
+ int ret;
+ json_t *root = json_object();
+
+ if (!root) {
+ TELEMETRY_LOG_ERR("Error - Could not create root json "
+ "object\n");
+ goto fail;
+ }
+
+ if (inv_choice == INV_ACTION_FIELD) {
+ ret = json_object_set_new(root, "ac--on", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting invalid action "
+ "field in root object failed\n");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "action", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting valid action field "
+ "in root object failed\n");
+ goto fail;
+ }
+ }
+
+ if (inv_choice == INV_COMMAND_FIELD) {
+ ret = json_object_set_new(root, "co---nd",
+ json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting invalid command "
+ "field in root object failed\n");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "command",
+ json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting valid command "
+ "field in root object failed\n");
+ goto fail;
+ }
+ }
+
+ json_t *data = json_null();
+ if (client_path) {
+ data = json_object();
+ if (!data) {
+ TELEMETRY_LOG_ERR("Error - Data json object creation "
+ "failed\n");
+ goto fail;
+ }
+
+ ret = json_object_set_new(data, "client_path",
+ json_string(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting valid client_path "
+ "field in data object failed\n");
+ goto fail;
+ }
+
+ } else if (port_ids) {
+ ret = rte_telemetry_format_port_stat_ids(port_ids, num_port_ids,
+ stat_names, num_stat_names, &data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Formatting Port/Stat "
+ "arrays failed\n");
+ goto fail;
+ }
+
+ }
+
+ if (inv_choice == INV_DATA_FIELD) {
+ ret = json_object_set_new(root, "d--a", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting invalid data "
+ "field in data object failed\n");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "data", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Setting valid data field "
+ "in data object failed\n");
+ goto fail;
+ }
+ }
+
+ *request = json_dumps(root, 0);
+ if (!*request) {
+ TELEMETRY_LOG_ERR("Error - Converting JSON root object to "
+ "char* failed\n");
+ goto fail;
+ }
+
+ json_decref(root);
+ return 0;
+
+fail:
+ if (root)
+ json_decref(root);
+ return -1;
+}
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice)
+{
+ int ret;
+ char *request;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Telemetry argument has not been "
+ "initialised\n");
+ return -EINVAL;
+ }
+
+ char *client_path_data = NULL;
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command_choice = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path_data = "INVALID_DATA";
+
+ ret = rte_telemetry_create_json_request(action_choice, command_choice,
+ client_path_data, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not create JSON Request\n");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Could not parse JSON Request\n");
+ return -1;
+ }
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice)
+{
+ int ret;
+ char *request;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Telemetry argument has not been "
+ "initialised\n");
+ return -EINVAL;
+ }
+
+ char *command = "ports_details";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ port_ids = NULL;
+
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not create JSON Request\n");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Could not parse JSON Request\n");
+ return -1;
+ }
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names,
+ int inv_choice)
+{
+ int ret;
+ char *request;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Telemetry argument has not been "
+ "initialised\n");
+ return -EINVAL;
+ }
+
+ char *command = "ports_stats_values_by_name";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL) {
+ port_ids = NULL;
+ stat_names = NULL;
+ }
+
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, stat_names, num_stat_names,
+ &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not create JSON Request\n");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Could not parse JSON Request\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_unreg_request(struct telemetry_impl *telemetry,
+ int action_choice, const char *client_path, int inv_choice)
+{
+ int ret;
+ char *request;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Telemetry argument has not been "
+ "initialised\n");
+ return -EINVAL;
+ }
+
+ char *command = "clients";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path = NULL;
+
+ ret = rte_telemetry_create_json_request(action_choice, command,
+ client_path, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not create JSON Request\n");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Warning - Could not parse JSON Request\n");
+ return -1;
+ }
+ return 0;
+}
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Error - Telemetry argument has not been "
+ "initialised\n");
+ return -EINVAL;
+ }
+
+ const char *client_path = TEST_CLIENT;
+ ret = rte_telemetry_create_test_socket(telemetry, client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Could not create test request "
+ "client socket\n");
+ return -1;
+ }
+
+ int port_ids[] = {0, 1};
+ int num_port_ids = RTE_DIM(port_ids);
+
+ static const char * const stat_names[] = {"tx_good_packets",
+ "rx_good_packets"};
+ int num_stat_names = RTE_DIM(stat_names);
+
+ static const char * const test_types[] = {
+ "INVALID ACTION VALUE TESTS",
+ "INVALID COMMAND VALUE TESTS",
+ "INVALID DATA VALUE TESTS",
+ "INVALID ACTION FIELD TESTS",
+ "INVALID COMMAND FIELD TESTS",
+ "INVALID DATA FIELD TESTS",
+ "INVALID JSON FORMAT TESTS",
+ "VALID TESTS"
+ };
+
+
+#define NUM_TEST_TYPES (sizeof(test_types)/sizeof(const char * const))
+
+ uint32_t i;
+ for (i = 0; i < NUM_TEST_TYPES; i++) {
+ TELEMETRY_LOG_INFO("%s\n",
+ test_types[i]);
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "ports", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports valid test "
+ "failed\n");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports invalid test"
+ " failed\n");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports test passed\n");
+
+ ret = rte_telemetry_send_get_ports_details_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports details valid\n");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports details "
+ "invalid\n");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports details test passed\n");
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "port_stats", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get port stats valid "
+ "test\n");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports stats invalid"
+ " test failed\n");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats test passed\n");
+
+ ret = rte_telemetry_send_stats_values_by_name_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids,
+ stat_names, num_stat_names, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports stats values by"
+ " name valid test failed\n");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Get ports stats values by"
+ " name invalid test failed\n");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats values by name"
+ " test passed\n");
+
+ ret = rte_telemetry_send_unreg_request(telemetry, ACTION_DELETE,
+ client_path, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Deregister valid test "
+ "failed\n");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Error - Deregister invalid test"
+ " failed\n");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Deregister test passed\n");
+ }
+
+ return 0;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.h b/lib/librte_telemetry/rte_telemetry_parser_test.h
new file mode 100644
index 0000000..6ada852
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#ifndef _RTE_TELEMETRY_PARSER_TEST_H_
+#define _RTE_TELEMETRY_PARSER_TEST_H_
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry);
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, json_t **data);
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, char **request,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice);
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_unreg_request(int action_choice, const char *client_path,
+ int inv_choice);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_socket_tests.h b/lib/librte_telemetry/rte_telemetry_socket_tests.h
new file mode 100644
index 0000000..db9167c
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_socket_tests.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdbool.h>
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_SOCKET_TESTING_H_
+#define _RTE_TELEMETRY_SOCKET_TESTING_H_
+
+int32_t
+rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry,
+ int fd);
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
+ int send_fd, int recv_fd);
+
+int32_t
+rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
+ int *recv_fd);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
index efd437d..5ce5680 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -2,5 +2,6 @@ DPDK_18.05 {
global:

rte_telemetry_init;
+ rte_telemetry_selftest;
local: *;
};
--
2.7.4
Stephen Hemminger
2018-08-23 23:15:37 UTC
Permalink
On Thu, 23 Aug 2018 13:08:09 +0100
Post by Ciara Power
This patch adds all tests for the Telemetry API.
The tests added include a parser test, selftest, and socket
messaging tests.
The parser tests pass valid and invalid messages to the parser
to ensure the correct return values are received.
The selftest tests basic functions in the Telemetry API such as
registering, unregistering, and initialisation.
The socket messaging tests pass messages through the socket and
validates the return message, to ensure the Telemetry API is
responding correctly.
---
drivers/telemetry/telemetry/telemetry_driver.c | 7 +
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
Please add entry to MAINTAINERS for new libraries.
Ciara Power
2018-08-23 12:08:10 UTC
Permalink
This patch adds functionality to run the selftest by passing
in an argument to vdev, "selftest".

To run the tests that were added in the previous patch, the
argument "selftest=1" must be added to the vdev arguments.
This will enable the user to run telemetry with or without tests,
as required.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
drivers/telemetry/telemetry/telemetry_driver.c | 50 ++++++++++++++++++++++----
lib/librte_telemetry/rte_telemetry.c | 35 +++++++++---------
lib/librte_telemetry/rte_telemetry.h | 8 ++++-
lib/librte_telemetry/rte_telemetry_internal.h | 1 +
4 files changed, 70 insertions(+), 24 deletions(-)

diff --git a/drivers/telemetry/telemetry/telemetry_driver.c b/drivers/telemetry/telemetry/telemetry_driver.c
index 125a89c..a97c199 100644
--- a/drivers/telemetry/telemetry/telemetry_driver.c
+++ b/drivers/telemetry/telemetry/telemetry_driver.c
@@ -8,21 +8,57 @@
#include <rte_malloc.h>
#include <rte_bus_vdev.h>
#include <rte_lcore.h>
+#include <rte_kvargs.h>
+
+#define SELFTEST_ARG "selftest"
+
+static int
+assign_selftest(const char *key __rte_unused, const char *value, void *opaque)
+{
+ int *selftest = opaque;
+ *selftest = atoi(value);
+ return 0;
+}

static int
telemetry_probe(struct rte_vdev_device *vdev)
{
+ static const char *const args[] = {
+ SELFTEST_ARG
+ };
+
+ const char *params;
int ret;
+ struct telemetry_args telemetry_args;
+ telemetry_args.selftest = 0;

- RTE_SET_USED(vdev);
- ret = rte_telemetry_selftest();
- if (ret < 0) {
- printf("Error - Selftest failed\n");
- return -1;
+ params = rte_vdev_device_args(vdev);
+ if (params != NULL && params[0] != '\0') {
+ struct rte_kvargs *kvlist = rte_kvargs_parse(params, args);
+
+ if (!kvlist) {
+ printf("Error - Could not create kvlist\n");
+ } else {
+ ret = rte_kvargs_process(kvlist, SELFTEST_ARG,
+ assign_selftest, &telemetry_args.selftest);
+ if (ret != 0) {
+ printf("Error - Processing the arguments\n");
+ rte_kvargs_free(kvlist);
+ return ret;
+ }
+ }
+ rte_kvargs_free(kvlist);
+ }
+ if (telemetry_args.selftest) {
+ ret = rte_telemetry_selftest();
+ if (ret < 0) {
+ printf("Error - Selftest failed\n");
+ return -1;
+ }
+ printf("Success - Selftest passed\n");
}
- printf("Success - Selftest passed\n");

- ret = rte_telemetry_init(rte_socket_id());
+ ret = rte_telemetry_init(rte_socket_id(), &telemetry_args);
if (ret < 0) {
printf("Error - Telemetry initialisation failed\n");
return -1;
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index ecf644b..1f7528d 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -666,18 +666,21 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
return -1;
}
telemetry->metrics_register_done = 1;
- ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
- telemetry->server_fd);
- if (ret < 0)
- return -1;
+ if (telemetry->selftest) {
+ ret =
+ rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ telemetry->server_fd);
+ if (ret < 0)
+ return -1;

- ret = rte_telemetry_parser_test(telemetry);
- if (ret < 0) {
- TELEMETRY_LOG_ERR("Error - Parser Tests Failed\n");
- return -1;
+ ret = rte_telemetry_parser_test(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Error - Parser Tests Failed\n");
+ return -1;
+ }
+ TELEMETRY_LOG_INFO("Success - All Parser Tests Passed\n");
+ telemetry->selftest = 0;
}
- TELEMETRY_LOG_INFO("Success - All Parser Tests Passed\n");
-
return 0;
}

@@ -856,7 +859,7 @@ rte_telemetry_create_socket(struct telemetry_impl *telemetry)
}

int32_t
-rte_telemetry_init(uint32_t socket_id)
+rte_telemetry_init(uint32_t socket_id, struct telemetry_args *args)
{
int ret;
pthread_attr_t attr;
@@ -873,7 +876,7 @@ rte_telemetry_init(uint32_t socket_id)
TELEMETRY_LOG_ERR("Error - Memory could not be allocated\n");
return -ENOMEM;
}
-
+ static_telemetry->selftest = args->selftest;
static_telemetry->socket_id = socket_id;
rte_metrics_init(static_telemetry->socket_id);
ret = rte_telemetry_create_socket(static_telemetry);
@@ -1167,10 +1170,10 @@ rte_telemetry_selftest(void)
const char *invalid_client_path = SELFTEST_INVALID_CLIENT;
const char *valid_client_path = SELFTEST_VALID_CLIENT;
int ret, sockfd;
+ struct telemetry_args args;
+ args.selftest = 0;

- TELEMETRY_LOG_INFO("Selftest\n");
-
- ret = rte_telemetry_init(rte_socket_id());
+ ret = rte_telemetry_init(rte_socket_id(), &args);
if (ret < 0) {
TELEMETRY_LOG_ERR("Error - Valid initialisation test"
" failed\n");
@@ -1178,7 +1181,7 @@ rte_telemetry_selftest(void)
}
TELEMETRY_LOG_INFO("Success - Valid initialisation test passed\n");

- ret = rte_telemetry_init(rte_socket_id());
+ ret = rte_telemetry_init(rte_socket_id(), &args);
if (ret != -EALREADY) {
TELEMETRY_LOG_ERR("Error - Invalid initialisation test "
"failed\n");
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
index 4f187b7..ac66595 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -7,12 +7,18 @@
#ifndef _RTE_TELEMETRY_H_
#define _RTE_TELEMETRY_H_

+typedef struct telemetry_args {
+ int selftest;
+} telemetry_args;
+
/**
* Get the telemetry_impl structure device pointer initialised.
*
* @param socket_id
* Unsigned integer representing the socket id to be used
* for the telemetry structure.
+ * @param args
+ * Struct containing arguments from telemetry_args
*
* @return
* 0 on successful initialisation.
@@ -24,7 +30,7 @@
* -EALREADY if Telemetry is already initialised.
*/
int32_t
-rte_telemetry_init(uint32_t socket_id);
+rte_telemetry_init(uint32_t socket_id, struct telemetry_args *args);

/**
* Clean up and free memory.
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 3e21b79..3270711 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -41,6 +41,7 @@ typedef struct telemetry_impl {
TAILQ_HEAD(, telemetry_client) client_list_head;
struct telemetry_client *request_client;
int register_fail_count;
+ int selftest;
} telemetry_impl;

int32_t
--
2.7.4
Ciara Power
2018-08-23 12:08:11 UTC
Permalink
This patch adds all documentation for telemetry.

A description on how to use the Telemetry API with a DPDK
application is given in this document.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 86 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 87 insertions(+)
create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/doc/guides/howto/index.rst b/doc/guides/howto/index.rst
index e13a090..a642a2b 100644
--- a/doc/guides/howto/index.rst
+++ b/doc/guides/howto/index.rst
@@ -17,3 +17,4 @@ HowTo Guides
virtio_user_for_container_networking
virtio_user_as_exceptional_path
packet_capture_framework
+ telemetry
diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst
new file mode 100644
index 0000000..0a2ae97
--- /dev/null
+++ b/doc/guides/howto/telemetry.rst
@@ -0,0 +1,86 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright(c) 2018 Intel Corporation.
+
+DPDK Telemetry API User Guide
+==============================
+
+This document describes how the Data Plane Development Kit(DPDK) Telemetry API
+is used for querying port statistics from incoming traffic.
+
+Introduction
+------------
+
+The ``librte_telemetry`` provides the functionality so that users may query
+metrics from incoming port traffic. The application which initializes packet
+forwarding will act as the server, sending metrics to the requesting application
+which acts as the client.
+
+In DPDK, applications can be used to initialize the ``telemetry`` as a virtual
+device. To view incoming traffic on featured ports, the application should be
+run first (ie. after ports are configured). Once the application is running, the
+service assurance agent(for example the collectd plugin) should be run to begin
+querying the API.
+
+A client connects their Service Assurance application to the DPDK application
+via a UNIX socket. Once a connection is established, a client can send JSON
+messages to the DPDK application requesting metrics via another UNIX client.
+This request is then handled and parsed if valid. The response is then
+formatted in JSON and sent back to the requesting client.
+
+Pre-requisites
+~~~~~~~~~~~~~~
+
+* Python ≥ 2.5
+
+* Jansson library for JSON serialization
+
+Test Environment
+----------------
+
+``telemetry`` offers a range of selftests that a client can run within
+the DPDK application. This is done by passing in an argument to the
+virtual device initialization like so through the EAL command line.::
+
+ ./dpdk_app --vdev=telemetry,selftest=1
+
+
+Configuration
+-------------
+
+Enable the telemetry API by modifying the following config option before
+building DPDK::
+
+ CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+Note: Meson will pick this up automatically if ``libjansson`` is available.
+
+Running the Application
+-----------------------
+
+The following steps demonstrate how to run the ``telemetry`` API to query all
+statistics on all active ports, using the ``telemetry_client`` python script
+to query.
+Note: This guide assumes packet generation is applicable and the user is
+testing with testpmd as a DPDK primary application to forward packets, although
+any DPDK application is applicable.
+
+#. Launch testpmd as the primary application with the ``telemetry``
+ virtual device.::
+
+ ./app/testpmd --vdev=telemetry
+
+#. Launch the ``telemetry`` python script with a client filepath.::
+
+ python usertools/telemetry_client.py /var/run/some_client
+
+ The client filepath is going to be used to setup our UNIX connect with the
+ DPDK primary application, in this case ``testpmd``
+ This will initialize a menu where a client can proceed to recursively query
+ statistics, request statistics once or unregister the file_path, thus exiting
+ the menu.
+
+#. Send traffic to any or all available ports from a traffic generator.
+ Select a query option(recursive or singular polling).
+ The metrics will then be displayed on the client terminal in JSON format.
+
+#. Once finished, unregister the client using the menu command.
--
2.7.4
Kovacevic, Marko
2018-09-25 08:53:42 UTC
Permalink
Post by Ciara Power
This patch adds all documentation for telemetry.
A description on how to use the Telemetry API with a DPDK application is
given in this document.
---
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 86
++++++++++++++++++++++++++++++++++++++++++
Acked-by: Marko Kovacevic <marko.kovace
Ciara Power
2018-08-23 12:08:12 UTC
Permalink
This patch adds a python script which can be used as a demo
client. The script is interactive and will allow the user to
register, request statistics, and unregister.

To run the script, an argument for the client file path must
be passed in: "python telemetry_client.py "file_path".

This script is useful to see how the Telemetry API for DPDK
is used, and how to make the initial connection.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
usertools/telemetry_client.py | 116 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 usertools/telemetry_client.py

diff --git a/usertools/telemetry_client.py b/usertools/telemetry_client.py
new file mode 100644
index 0000000..ede778c
--- /dev/null
+++ b/usertools/telemetry_client.py
@@ -0,0 +1,116 @@
+# SPDK-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+import socket
+import os
+import sys
+import time
+
+BUFFER_SIZE = 200000
+
+METRICS_REQ = "{\"action\":0,\"command\":\"ports_all_stat_values\",\"data\":null}"
+API_REG = "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\":\""
+API_UNREG = "{\"action\":2,\"command\":\"clients\",\"data\":{\"client_path\":\""
+DEFAULT_FP = "/var/run/.default_client"
+
+class Socket:
+
+ def __init__(self):
+ self.send_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.client_fd = None
+
+ def __del__(self):
+ try:
+ self.send_fd.close()
+ self.recv_fd.close()
+ self.client_fd.close()
+ except:
+ print("Error - Sockets could not be closed")
+
+class Client:
+
+ def __init__(self): # Creates a client instance
+ self.socket = Socket()
+ self.file_path = None
+ self.choice = None
+ self.unregistered = 0
+
+ def __del__(self):
+ try:
+ if self.unregistered == 0:
+ self.unregister();
+ except:
+ print("Error - Client could not be destroyed")
+
+ def getFilepath(self, file_path): # Gets arguments from Command-Line and assigns to instance of client
+ self.file_path = file_path
+
+ def register(self): # Connects a client to DPDK-instance
+ if os.path.exists(self.file_path):
+ os.unlink(self.file_path)
+ try:
+ self.socket.recv_fd.bind(self.file_path)
+ except socket.error as msg:
+ print ("Error - Socket binding error: " + str(msg) + "\n")
+ self.socket.recv_fd.settimeout(2)
+ self.socket.send_fd.connect("/var/run/.rte_telemetry")
+ JSON = (API_REG + self.file_path + "\"}}")
+ self.socket.send_fd.sendall(JSON)
+ self.socket.recv_fd.listen(1)
+ self.socket.client_fd = self.socket.recv_fd.accept()[0]
+
+ def unregister(self): # Unregister a given client
+ self.socket.client_fd.send(API_UNREG + self.file_path + "\"}}")
+ self.socket.client_fd.close()
+
+ def requestMetrics(self): # Requests metrics for given client
+ self.socket.client_fd.send(METRICS_REQ)
+ data = self.socket.client_fd.recv(BUFFER_SIZE)
+ print "\nResponse: \n", str(data)
+
+ def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client
+ print("\nPlease enter the number of times you'd like to continuously request Metrics:")
+ n_requests = int(input("\n:"))
+ print("\033[F") #Removes the user input from screen, cleans it up
+ print("\033[K")
+ for i in range(n_requests):
+ self.requestMetrics()
+ time.sleep(sleep_time)
+
+ def interactiveMenu(self, sleep_time): # Creates Interactive menu within the script
+ while self.choice != 3:
+ print("\nOptions Menu")
+ print("[1] Send for Metrics for all ports")
+ print("[2] Send for Metrics for all ports recursively")
+ print("[3] Unregister client")
+
+ try:
+ self.choice = int(input("\n:"))
+ print("\033[F") #Removes the user input for screen, cleans it up
+ print("\033[K")
+ if self.choice == 1:
+ self.requestMetrics()
+ elif self.choice == 2:
+ self.repeatedlyRequestMetrics(sleep_time)
+ elif self.choice == 3:
+ self.unregister()
+ self.unregistered = 1
+ else:
+ print("Error - Invalid request choice")
+ except:
+ pass
+
+if __name__ == "__main__":
+
+ sleep_time = 1
+ file_path = ""
+ if (len(sys.argv) == 2):
+ file_path = sys.argv[1]
+ else:
+ print("Warning - No filepath passed, using default (" + DEFAULT_FP + ").")
+ file_path = DEFAULT_FP
+ client = Client()
+ client.getFilepath(file_path)
+ client.register()
+ client.interactiveMenu(sleep_time)
--
2.7.4
Ciara Power
2018-08-23 12:08:13 UTC
Permalink
This patch adds the patch for the collectd plugin developed for use
with the DPDK Telemetry library. The patch is included here to allow
users to apply the patch to collectd when using DPDK Telemetry.
Further details on applying the collectd patch can be found in the
collectd patch commit message.

The collectd plugin will be upstreamed to collectd at a later stage.

Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Emma Kenny <***@intel.com>
---
...emetry-add-plugin-for-DPDK-metrics-via-DP.patch | 578 +++++++++++++++++++++
1 file changed, 578 insertions(+)
create mode 100644 lib/librte_telemetry/collectd_plugin/v1-0001-dpdk_telemetry-add-plugin-for-DPDK-metrics-via-DP.patch

diff --git a/lib/librte_telemetry/collectd_plugin/v1-0001-dpdk_telemetry-add-plugin-for-DPDK-metrics-via-DP.patch b/lib/librte_telemetry/collectd_plugin/v1-0001-dpdk_telemetry-add-plugin-for-DPDK-metrics-via-DP.patch
new file mode 100644
index 0000000..e81d463
--- /dev/null
+++ b/lib/librte_telemetry/collectd_plugin/v1-0001-dpdk_telemetry-add-plugin-for-DPDK-metrics-via-DP.patch
@@ -0,0 +1,578 @@
+From 4c7660827b471ecd862122aa0e2a90c0a0f8ec97 Mon Sep 17 00:00:00 2001
+From: Emma Kenny <***@intel.com>
+Date: Tue, 21 Aug 2018 15:49:15 +0100
+Subject: [PATCH v1] dpdk_telemetry: add plugin for DPDK metrics via DPDK
+ Telemetry library
+
+This patch introduces a new plugin for collectd, which consumes DPDK metrics
+via the dpdk_telemetry library. The collectd plugin here provides an
+easy way to use the DPDK telemetry API to query ethernet device metrics.
+
+The collectd plugin retrieves metrics from a DPDK packet forwarding
+application by sending a JSON formatted message via a UNIX domain
+socket. The DPDK telemetry component will respond with a JSON formatted
+reply, delivering the requested metrics. The dpdk_telemetry collectd
+plugin parses the JSON data, and publishes the metric values to collectd
+for further use.
+
+This plugin has a dependency on the DPDK Telemetry library, as it must be
+"in sync" with the DPDK Telemetry implementation.
+
+This patch should be applied on the following collectd commit:
+fff795c9846bd8fe4bc7f76bcd83a2b8cefb4525
+
+Signed-off-by: Emma Kenny <***@intel.com>
+Signed-off-by: Brian Archbold <***@intel.com>
+---
+ Makefile.am | 7 +
+ configure.ac | 4 +
+ src/collectd.conf.in | 26 ++--
+ src/collectd.conf.pod | 35 +++++
+ src/dpdk_telemetry.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/types.db | 1 +
+ 6 files changed, 427 insertions(+), 10 deletions(-)
+ create mode 100755 src/dpdk_telemetry.c
+
+diff --git a/Makefile.am b/Makefile.am
+index cb40148..66885e2 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -884,6 +884,13 @@ dpdkstat_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
+ dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
+ dpdkstat_la_LIBADD = $(LIBDPDK_LIBS)
+ endif
++if BUILD_PLUGIN_DPDK_TELEMETRY
++pkglib_LTLIBRARIES += dpdk_telemetry.la
++dpdk_telemetry_la_SOURCES = src/dpdk_telemetry.c
++dpdk_telemetry_la_CFLAGS = $(AM_CFLAGS)
++dpdk_telemetry_la_LDFLAGS = $(PLUGIN_LDFLAGS)
++dpdk_telemetry_la_LIBADD = -ljansson
++endif
+
+ if BUILD_PLUGIN_DRBD
+ pkglib_LTLIBRARIES += drbd.la
+diff --git a/configure.ac b/configure.ac
+index 7bf3718..93ee1a0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -6289,6 +6289,7 @@ plugin_disk="no"
+ plugin_drbd="no"
+ plugin_dpdkevents="no"
+ plugin_dpdkstat="no"
++plugin_dpdk_telemetry="no"
+ plugin_entropy="no"
+ plugin_ethstat="no"
+ plugin_fhcount="no"
+@@ -6349,6 +6350,7 @@ if test "x$ac_system" = "xLinux"; then
+ plugin_cpufreq="yes"
+ plugin_disk="yes"
+ plugin_drbd="yes"
++ plugin_dpdk_telemetry="yes"
+ plugin_entropy="yes"
+ plugin_fhcount="yes"
+ plugin_fscache="yes"
+@@ -6710,6 +6712,7 @@ AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysi
+ AC_PLUGIN([dpdkevents], [$plugin_dpdkevents], [Events from DPDK])
+ AC_PLUGIN([dpdkstat], [$plugin_dpdkstat], [Stats from DPDK])
+ AC_PLUGIN([drbd], [$plugin_drbd], [DRBD statistics])
++AC_PLUGIN([dpdk_telemetry], [$plugin_dpdk_telemetry], [Metrics from DPDK Telemetry])
+ AC_PLUGIN([email], [yes], [EMail statistics])
+ AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics])
+ AC_PLUGIN([ethstat], [$plugin_ethstat], [Stats from NIC driver])
+@@ -7132,6 +7135,7 @@ AC_MSG_RESULT([ dns . . . . . . . . . $enable_dns])
+ AC_MSG_RESULT([ dpdkevents. . . . . . $enable_dpdkevents])
+ AC_MSG_RESULT([ dpdkstat . . . . . . $enable_dpdkstat])
+ AC_MSG_RESULT([ drbd . . . . . . . . $enable_drbd])
++AC_MSG_RESULT([ dpdk_telemetry. . . . $enable_dpdk_telemetry])
+ AC_MSG_RESULT([ email . . . . . . . . $enable_email])
+ AC_MSG_RESULT([ entropy . . . . . . . $enable_entropy])
+ AC_MSG_RESULT([ ethstat . . . . . . . $enable_ethstat])
+diff --git a/src/collectd.conf.in b/src/collectd.conf.in
+index af65214..8e9d500 100644
+--- a/src/collectd.conf.in
++++ b/src/collectd.conf.in
+@@ -62,12 +62,12 @@
+ @***@LoadPlugin logfile
+ @***@LoadPlugin log_logstash
+
+-#<Plugin logfile>
+-# LogLevel @DEFAULT_LOG_LEVEL@
+-# File STDOUT
+-# Timestamp true
+-# PrintSeverity false
+-#</Plugin>
++<Plugin logfile>
++ LogLevel @DEFAULT_LOG_LEVEL@
++ File STDOUT
++ Timestamp true
++ PrintSeverity false
++</Plugin>
+
+ #<Plugin log_logstash>
+ # LogLevel @DEFAULT_LOG_LEVEL@
+@@ -117,6 +117,7 @@
+ #@***@LoadPlugin dns
+ #@***@LoadPlugin dpdkevents
+ #@***@LoadPlugin dpdkstat
++@***@LoadPlugin dpdk_telemetry
+ #@***@LoadPlugin drbd
+ #@***@LoadPlugin email
+ #@***@LoadPlugin entropy
+@@ -394,10 +395,10 @@
+ # SubtractGuestState true
+ #</Plugin>
+ #
+-#<Plugin csv>
+-# DataDir "@localstatedir@/lib/@PACKAGE_NAME@/csv"
+-# StoreRates false
+-#</Plugin>
++<Plugin csv>
++ DataDir "@localstatedir@/lib/@PACKAGE_NAME@/csv"
++ StoreRates false
++</Plugin>
+
+ #<Plugin curl>
+ # <Page "stock_quotes">
+@@ -595,6 +596,11 @@
+ # PortName "interface2"
+ #</Plugin>
+
++<Plugin dpdk_telemetry>
++ ClientSocketPath "/var/run/.client"
++ DpdkSocketPath "/var/run/.rte_telemetry"
++</Plugin>
++
+ #<Plugin email>
+ # SocketFile "@localstatedir@/run/@PACKAGE_NAME@-email"
+ # SocketGroup "collectd"
+diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
+index 6e6d6ea..3cc062f 100644
+--- a/src/collectd.conf.pod
++++ b/src/collectd.conf.pod
+@@ -2822,6 +2822,41 @@ convention will be used for the additional ports.
+
+ =back
+
++=head2 Plugin C<dpdk_telemetry>
++
++
++The I< dpdk_telemetry > plugin collects DPDK metrics via the dpdk_telemetry library.
++
++
++B<Synopsis:>
++
++
++ <Plugin dpdk_telemetry>
++ ClientSocketPath "/var/run/.client"
++ DpdkSocketPath "/var/run/.rte_telemetry"
++ </Plugin>
++
++
++B<Options:>
++
++
++=over 2
++
++
++=item B<ClientSocketPath> I<Client_Path>
++
++
++The path to the client socket.
++
++
++=item B<DpdkSocketPath> I<Dpdk_Path>
++
++
++The path to DPDK Telemetry.
++
++
++=back
++
+ =head2 Plugin C<email>
+
+ =over 4
+diff --git a/src/dpdk_telemetry.c b/src/dpdk_telemetry.c
+new file mode 100755
+index 0000000..773256e
+--- /dev/null
++++ b/src/dpdk_telemetry.c
+@@ -0,0 +1,364 @@
++/*-
++ * collectd - src/dpdk_telemetry.c
++ * MIT License
++ *
++ * Copyright(c) 2018 Intel Corporation. All rights reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of
++ * this software and associated documentation files (the "Software"), to deal in
++ * the Software without restriction, including without limitation the rights to
++ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is furnished to
++ * do
++ * so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all
++ * copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ */
++
++#include "collectd.h"
++#include "common.h"
++#include "plugin.h"
++#include "utils_time.h"
++
++#include <errno.h>
++#include <jansson.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/queue.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/unistd.h>
++
++#define BUF_SIZE 1000000
++#define PLUGIN_NAME "dpdk_telemetry"
++#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
++#define DEFAULT_CLIENT_PATH "/var/run/.client"
++
++struct client_info {
++ int s_send;
++ int s_recv;
++ int fd;
++ const char *dpdk_path;
++ const char *client_path;
++ struct sockaddr_un addr;
++ struct sockaddr_un addrs;
++};
++
++static struct client_info *client = NULL;
++static char g_client_path[BUF_SIZE];
++static char g_dpdk_path[BUF_SIZE];
++
++static int dpdk_telemetry_config(oconfig_item_t *ci) {
++ int ret, i;
++
++ INFO(PLUGIN_NAME ": %s:%d", __FUNCTION__, __LINE__);
++
++ for (i = 0; i < ci->children_num; i++) {
++ oconfig_item_t *child = ci->children + i;
++
++ if (strcasecmp("ClientSocketPath", child->key) == 0) {
++ ret = cf_util_get_string_buffer(child, g_client_path,
++ sizeof(g_client_path));
++ } else if (strcasecmp("DpdkSocketPath", child->key) == 0) {
++ ret = cf_util_get_string_buffer(child, g_dpdk_path, sizeof(g_dpdk_path));
++ } else {
++ ERROR(PLUGIN_NAME ": Unknown configuration parameter"
++ "\"%s\"",
++ child->key);
++ ret = -1;
++ }
++
++ if (ret < 0) {
++ INFO(PLUGIN_NAME ": %s:%d ret =%d", __FUNCTION__, __LINE__, ret);
++ return ret;
++ }
++ }
++ return 0;
++}
++
++static int dpdk_telemetry_parse(json_t *stats, json_t *port, int portid) {
++ json_t *statsArrayObj;
++ if (!stats) {
++ ERROR("dpdk_telemetry: Stats pointer is invalid\n");
++ return -1;
++ }
++
++ if (!port) {
++ ERROR("dpdk_telemetry: Port pointer is invalid\n");
++ return -1;
++ }
++
++ if (portid < 0) {
++ ERROR("dpdk_telemetry: portid is invalid\n");
++ return -1;
++ }
++
++ json_t *name, *value;
++ const char *name_string;
++ int value_int, statslen, i;
++ statslen = json_array_size(stats);
++ for (i = 0; i < statslen; i++) {
++ statsArrayObj = json_array_get(stats, i);
++ name = json_object_get(statsArrayObj, "name");
++ value = json_object_get(statsArrayObj, "value");
++ if (!name) {
++ ERROR("dpdk_telemetry: Request does not have name field\n");
++ return -1;
++ }
++ if (!json_is_string(name)) {
++ ERROR("dpdk_telemetry: Stat name value is not a string\n");
++ return -1;
++ }
++ name_string = json_string_value(name);
++ if (!value) {
++ ERROR("dpdk_telemetry: Request does not have value name\n");
++ return -1;
++ }
++ if (!json_is_integer(value)) {
++ ERROR("dpdk_telemetry: Stat value is not an integer\n");
++ return -1;
++ }
++
++ char dev_name[BUF_SIZE];
++ snprintf(dev_name, sizeof(dev_name), "%s.%d", name_string, portid);
++ value_int = json_integer_value(value);
++ value_t dpdk_telemetry_values[1];
++ value_list_t dpdk_telemetry_vl = VALUE_LIST_INIT;
++ dpdk_telemetry_values[0].counter = value_int;
++ dpdk_telemetry_vl.values = dpdk_telemetry_values;
++ dpdk_telemetry_vl.values_len = 1;
++ dpdk_telemetry_vl.time = cdtime();
++ snprintf(dpdk_telemetry_vl.host, sizeof(dpdk_telemetry_vl.host), "%s",
++ hostname_g);
++ snprintf(dpdk_telemetry_vl.plugin, sizeof(dpdk_telemetry_vl.plugin),
++ "dpdk_telemetry");
++ snprintf(dpdk_telemetry_vl.plugin_instance,
++ sizeof(dpdk_telemetry_vl.plugin_instance), "%s", dev_name);
++ snprintf(dpdk_telemetry_vl.type, sizeof(dpdk_telemetry_vl.type),
++ "dpdk_telemetry");
++ snprintf(dpdk_telemetry_vl.type_instance,
++ sizeof(dpdk_telemetry_vl.type_instance), "%s", name_string);
++
++ int ret = plugin_dispatch_values(&dpdk_telemetry_vl);
++ if (ret < 0) {
++ ERROR("dpdk_telemetry: Failed to dispatch values");
++ return -1;
++ }
++ }
++ return 0;
++}
++
++static int parse_json(char *buf) {
++
++ if (!buf) {
++ ERROR("dpdk_telemetry: buf pointer is invalid\n");
++ return -1;
++ }
++ json_error_t error;
++ json_t *root = json_loads(buf, 0, &error);
++ int arraylen, i;
++ json_t *status, *dataArray, *stats, *dataArrayObj;
++ stats = NULL;
++
++ if (!root) {
++ ERROR("dpdk_telemetry: Could not load JSON object from data passed in"
++ " : %s\n",
++ error.text);
++ return -1;
++ } else if (!json_is_object(root)) {
++ ERROR("dpdk_telemetry: JSON Request is not a JSON object\n");
++ json_decref(root);
++ return -1;
++ }
++
++ status = json_object_get(root, "status_code");
++ if (!status) {
++ ERROR("dpdk_telemetry: Request does not have status field\n");
++ return -1;
++ } else if (!json_is_string(status)) {
++ ERROR("dpdk_telemetry: Status value is not a string\n");
++ return -1;
++ }
++ dataArray = json_object_get(root, "data");
++ if (!dataArray) {
++ ERROR("dpdk_telemetry: Request does not have data field\n");
++ return -1;
++ }
++ arraylen = json_array_size(dataArray);
++ if (!arraylen) {
++ ERROR("dpdk_telemetry: No data to get\n");
++ return -1;
++ }
++
++ for (i = 0; i < arraylen; i++) {
++ json_t *port;
++ dataArrayObj = json_array_get(dataArray, i);
++ port = json_object_get(dataArrayObj, "port");
++ stats = json_object_get(dataArrayObj, "stats");
++ if (!port) {
++ ERROR("dpdk_telemetry: Request does not have port field\n");
++ return -1;
++ }
++ if (!json_is_integer(port)) {
++ ERROR("dpdk_telemetry: Port value is not an integer\n");
++ return -1;
++ }
++
++ if (!stats) {
++ ERROR("dpdk_telemetry: Request does not have stats field\n");
++ return -1;
++ }
++ dpdk_telemetry_parse(stats, port, i);
++ }
++ return 0;
++}
++
++static int dpdk_telemetry_cleanup(void) {
++ if (!client) {
++ WARNING("dpdk_telemetry: instance pointer is NULL, cleanup() has already "
++ "been called\n");
++ return -1;
++ }
++ close(client->s_send);
++ close(client->s_recv);
++ close(client->fd);
++ free(client);
++ client = NULL;
++ return 0;
++}
++
++static int dpdk_telemetry_read(user_data_t *ud) {
++ INFO(PLUGIN_NAME ": %s:%d", __FUNCTION__, __LINE__);
++ struct client_info *client = (struct client_info *)ud->data;
++ char buffer[BUF_SIZE];
++ int bytes, ret;
++ char *json_string = "{\"action\":0,\"command\":"
++ "\"ports_all_stat_values\",\"data\":null}";
++ if (send(client->fd, json_string, strlen(json_string), 0) < 0) {
++ ERROR("dpdk_telemetry: Could not send stats\n");
++ return -1;
++ }
++ bytes = recv(client->fd, buffer, sizeof(buffer), 0);
++ buffer[bytes] = '\0';
++ if (bytes < 0) {
++ ERROR("dpdk_telemetry: Could not receive stats\n");
++ return -1;
++ }
++ ret = parse_json(buffer);
++ if (ret < 0) {
++ ERROR("dpdk_telemetry: Parsing failed\n");
++ return -1;
++ }
++ return 0;
++}
++
++static int dpdk_telemetry_init(void) {
++ INFO(PLUGIN_NAME ": %s:%d", __FUNCTION__, __LINE__);
++ char message[BUF_SIZE];
++
++ client = calloc(1, sizeof(struct client_info));
++ if (!client) {
++ ERROR("dpdk_telemetry: Memory could not be allocated\n");
++ return -1;
++ }
++ /*Here we look up the length of the g_dpdk_path string
++ * If it has a length we use it, otherwise we fall back to default
++ * See dpdk_telemetry_config() for details
++ */
++ client->dpdk_path = (strlen(g_dpdk_path)) ? g_dpdk_path : DEFAULT_DPDK_PATH;
++ client->client_path =
++ (strlen(g_client_path)) ? g_client_path : DEFAULT_CLIENT_PATH;
++ client->s_send = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (client->s_send < 0) {
++ ERROR("dpdk_telemetry: Failed to open socket\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ client->s_recv = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (client->s_recv < 0) {
++ ERROR("dpdk_telemetry: Failed to open message socket\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ client->addr.sun_family = AF_UNIX;
++ snprintf(client->addr.sun_path, sizeof(client->addr.sun_path), "%s",
++ client->dpdk_path);
++ if (connect(client->s_send, (struct sockaddr *)&client->addr,
++ sizeof(client->addr)) < 0) {
++ ERROR("dpdk_telemetry: Failed to connect\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ client->addrs.sun_family = AF_UNIX;
++ snprintf(client->addrs.sun_path, sizeof(client->addrs.sun_path), "%s",
++ client->client_path);
++ unlink(client->client_path);
++ if (bind(client->s_recv, (struct sockaddr *)&client->addrs,
++ sizeof(client->addrs)) < 0) {
++ ERROR("dpdk_telemetry: Failed to bind\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ if (listen(client->s_recv, 1) < 0) {
++ ERROR("dpdk_telemetry: Listen failed\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ snprintf(message, sizeof(message), "{\"action\":1,\"command\":\"clients\""
++ ",\"data\":{\"client_path\":\"%s\"}}",
++ client->client_path);
++ if (send(client->s_send, message, strlen(message), 0) < 0) {
++ ERROR("dpdk_telemetry: Could not send register message\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ client->fd = accept(client->s_recv, NULL, NULL);
++ if (client->fd < 0) {
++ ERROR("dpdk_telemetry: Failed to accept\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ user_data_t ud;
++ memset(&ud, 0, sizeof(ud));
++ ud.data = (void *)client;
++ plugin_register_complex_read(NULL, "dpdk_telemetry", dpdk_telemetry_read, 0,
++ &ud);
++ return 0;
++}
++
++static int dpdk_telemetry_shutdown(void) {
++ INFO(PLUGIN_NAME ": %s:%d", __FUNCTION__, __LINE__);
++ char msg[BUF_SIZE];
++ int ret;
++
++ snprintf(msg, sizeof(msg), "{\"action\":2,\"command\":\"clients\""
++ ",\"data\":{\"client_path\":\"%s\"}}",
++ client->client_path);
++ ret = send(client->fd, msg, strlen(msg), 0);
++ if (ret < 0) {
++ ERROR("dpdk_telemetry: Could not send unregister message\n");
++ dpdk_telemetry_cleanup();
++ return -1;
++ }
++ dpdk_telemetry_cleanup();
++ return 0;
++}
++
++void module_register(void) {
++ plugin_register_init("dpdk_telemetry", dpdk_telemetry_init);
++ plugin_register_complex_config("dpdk_telemetry", dpdk_telemetry_config);
++ plugin_register_shutdown("dpdk_telemetry", dpdk_telemetry_shutdown);
++}
+diff --git a/src/types.db b/src/types.db
+index f4933ee..4517b3c 100644
+--- a/src/types.db
++++ b/src/types.db
+@@ -76,6 +76,7 @@ dns_transfer value:DERIVE:0:U
+ dns_update value:DERIVE:0:U
+ dns_zops value:DERIVE:0:U
+ domain_state state:GAUGE:0:U, reason:GAUGE:0:U
++dpdk_telemetry value:DERIVE:0:U
+ drbd_resource value:DERIVE:0:U
+ duration seconds:GAUGE:0:U
+ email_check value:GAUGE:0:U
+--
+2.9.5
+
--
2.7.4
Thomas Monjalon
2018-09-18 09:52:04 UTC
Permalink
Post by Ciara Power
This patch adds the patch for the collectd plugin developed for use
with the DPDK Telemetry library. The patch is included here to allow
users to apply the patch to collectd when using DPDK Telemetry.
Further details on applying the collectd patch can be found in the
collectd patch commit message.
The collectd plugin will be upstreamed to collectd at a later stage.
Yes it must be upstreamed to collectd.
So no need to add it in DPDK. OK to remove it in v2?
Laatz, Kevin
2018-09-19 11:09:56 UTC
Permalink
Post by Thomas Monjalon
Post by Ciara Power
This patch adds the patch for the collectd plugin developed for use
with the DPDK Telemetry library. The patch is included here to allow
users to apply the patch to collectd when using DPDK Telemetry.
Further details on applying the collectd patch can be found in the
collectd patch commit message.
The collectd plugin will be upstreamed to collectd at a later stage.
Yes it must be upstreamed to collectd.
So no need to add it in DPDK. OK to remove it in v2?
Yes, will remove for v2.

Thanks,
Kevin
Kevin Laatz
2018-10-03 17:36:03 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the infrastructure and initial code for the telemetry
library.

The telemetry init is registered with eal_init(). We can then check to see
if --telemetry was passed as an eal flag. If --telemetry was parsed, then
we call telemetry init at the end of eal init.

Control threads are used to get CPU cycles for telemetry, which are
configured in this patch also.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
config/common_base | 5 ++
lib/Makefile | 2 +
lib/librte_eal/common/include/rte_eal.h | 19 ++++
lib/librte_eal/linuxapp/eal/eal.c | 37 +++++++-
lib/librte_eal/rte_eal_version.map | 7 ++
lib/librte_telemetry/Makefile | 28 ++++++
lib/librte_telemetry/meson.build | 7 ++
lib/librte_telemetry/rte_telemetry.c | 117 +++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 36 ++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 32 +++++++
lib/librte_telemetry/rte_telemetry_version.map | 6 ++
lib/meson.build | 2 +-
mk/rte.app.mk | 1 +
13 files changed, 297 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map

diff --git a/config/common_base b/config/common_base
index 4bcbaf9..682f8bf 100644
--- a/config/common_base
+++ b/config/common_base
@@ -716,6 +716,11 @@ CONFIG_RTE_LIBRTE_HASH=y
CONFIG_RTE_LIBRTE_HASH_DEBUG=n

#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+#
# Compile librte_efd
#
CONFIG_RTE_LIBRTE_EFD=y
diff --git a/lib/Makefile b/lib/Makefile
index afa604e..8cbd035 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev

ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index e114dcb..5929a34 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -498,6 +498,25 @@ enum rte_iova_mode rte_eal_iova_mode(void);
const char *
rte_eal_mbuf_user_pool_ops(void);

+typedef int (*rte_lib_init_fn)(void);
+
+typedef struct rte_lib_init_params {
+ TAILQ_ENTRY(rte_lib_init_params) next;
+ char eal_flag[32];
+ char help_text[80];
+ rte_lib_init_fn lib_init;
+ int enabled;
+} rte_lib_init_params;
+
+/**
+ * @internal Register a libraries init function
+ *
+ * @param reg_init
+ * Structure containing the eal flag, the lib help string and the init
+ * function pointer for the library.
+ */
+void rte_lib_init_register(struct rte_lib_init_params *reg_init);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index e59ac65..b9113c7 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -97,6 +97,13 @@ static char runtime_dir[PATH_MAX];

static const char *default_runtime_dir = "/var/run";

+TAILQ_HEAD(rte_lib_init_list, rte_lib_init_params);
+
+struct rte_lib_init_list rte_lib_init_list =
+ TAILQ_HEAD_INITIALIZER(rte_lib_init_list);
+
+rte_lib_init_params *lib_init_params;
+
int
eal_create_runtime_dir(void)
{
@@ -570,7 +577,7 @@ eal_log_level_parse(int argc, char **argv)
static int
eal_parse_args(int argc, char **argv)
{
- int opt, ret;
+ int opt, ret, valid_opt;
char **argvopt;
int option_index;
char *prgname = argv[0];
@@ -580,12 +587,27 @@ eal_parse_args(int argc, char **argv)

argvopt = argv;
optind = 1;
+ opterr = 0;

while ((opt = getopt_long(argc, argvopt, eal_short_options,
eal_long_options, &option_index)) != EOF) {

/* getopt is not happy, stop right now */
if (opt == '?') {
+ valid_opt = 0;
+ /* Check if the flag is in the registered lib inits */
+ TAILQ_FOREACH(lib_init_params, &rte_lib_init_list, next) {
+ if (strcmp(argv[optind-1],
+ lib_init_params->eal_flag) == 0) {
+ lib_init_params->enabled = 1;
+ valid_opt = 1;
+ opterr = 0;
+ }
+ }
+
+ if (valid_opt)
+ continue;
+
eal_usage(prgname);
ret = -1;
goto out;
@@ -786,6 +808,13 @@ static void rte_eal_init_alert(const char *msg)
RTE_LOG(ERR, EAL, "%s\n", msg);
}

+void
+rte_lib_init_register(struct rte_lib_init_params *reg_init)
+{
+ TAILQ_INSERT_HEAD(&rte_lib_init_list, reg_init, next);
+}
+
+
/* Launch threads, called at application init(). */
int
rte_eal_init(int argc, char **argv)
@@ -1051,6 +1080,12 @@ rte_eal_init(int argc, char **argv)

rte_eal_mcfg_complete();

+ /* Call the init function for each registered and enabled lib */
+ TAILQ_FOREACH(lib_init_params, &rte_lib_init_list, next) {
+ if (lib_init_params->enabled)
+ lib_init_params->lib_init();
+ }
+
return fctret;
}

diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 344a43d..914d0fa 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -262,6 +262,13 @@ DPDK_18.08 {

} DPDK_18.05;

+DPDK_18.11 {
+ global:
+
+ rte_lib_init_register;
+
+} DPDK_18.08;
+
EXPERIMENTAL {
global:

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..0d61361
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+LDLIBS += -lpthread
+LDLIBS += -ljansson
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
new file mode 100644
index 0000000..d9ffec2
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = userdata;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_WARN("TELEMETRY could not be initialised");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+*rte_telemetry_run_thread_func(void *userdata)
+{
+ int ret;
+ struct telemetry_impl *telemetry = userdata;
+
+ if (!telemetry) {
+ 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);
+}
+
+int32_t
+rte_telemetry_init()
+{
+ 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) {
+ TELEMETRY_LOG_ERR("Memory could not be allocated");
+ return -ENOMEM;
+ }
+
+ static_telemetry->socket_id = rte_socket_id();
+ rte_metrics_init(static_telemetry->socket_id);
+ pthread_attr_init(&attr);
+ 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;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_register);
+
+static struct rte_lib_init_params lib_init_params = {
+ .eal_flag = "--telemetry",
+ .help_text = "Telemetry lib",
+ .lib_init = &rte_telemetry_init,
+ .enabled = 0
+};
+
+static void
+rte_telemetry_register(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+
+ rte_lib_init_register(&lib_init_params);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 0000000..f7ecb7b
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * Get the telemetry_impl structure device pointer initialised.
+ *
+ * @return
+ * 0 on successful initialisation.
+ * @return
+ * -ENOMEM on memory allocation error
+ * @return
+ * -EPERM on unknown error failure
+ * @return
+ * -EALREADY if Telemetry is already initialised.
+ */
+int32_t
+rte_telemetry_init(void);
+
+/**
+ * Clean up and free memory.
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -EPERM on failure
+ */
+int32_t
+rte_telemetry_cleanup(void);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
new file mode 100644
index 0000000..4e810a8
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <rte_log.h>
+
+#ifndef _RTE_TELEMETRY_INTERNAL_H_
+#define _RTE_TELEMETRY_INTERNAL_H_
+
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
+ uint32_t socket_id;
+} telemetry_impl;
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
new file mode 100644
index 0000000..992d227
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.11 {
+ global:
+
+ rte_telemetry_init;
+ local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index eb91f10..fc84b2f 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -24,7 +24,7 @@ libraries = [ 'compat', # just a header, used for versioning
# add pkt framework libs which use other libs from above
'port', 'table', 'pipeline',
# flow_classify lib depends on pkt framework table lib
- 'flow_classify', 'bpf']
+ 'flow_classify', 'bpf', 'telemetry']

default_cflags = machine_args
if cc.has_argument('-Wno-format-truncation')
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index de33883..1223a85 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,6 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
Gaëtan Rivet
2018-10-04 14:13:04 UTC
Permalink
Hi,
Post by Kevin Laatz
This patch adds the infrastructure and initial code for the telemetry
library.
The telemetry init is registered with eal_init(). We can then check to see
if --telemetry was passed as an eal flag. If --telemetry was parsed, then
we call telemetry init at the end of eal init.
Control threads are used to get CPU cycles for telemetry, which are
configured in this patch also.
---
config/common_base | 5 ++
lib/Makefile | 2 +
lib/librte_eal/common/include/rte_eal.h | 19 ++++
lib/librte_eal/linuxapp/eal/eal.c | 37 +++++++-
lib/librte_eal/rte_eal_version.map | 7 ++
lib/librte_telemetry/Makefile | 28 ++++++
lib/librte_telemetry/meson.build | 7 ++
lib/librte_telemetry/rte_telemetry.c | 117 +++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 36 ++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 32 +++++++
lib/librte_telemetry/rte_telemetry_version.map | 6 ++
lib/meson.build | 2 +-
mk/rte.app.mk | 1 +
13 files changed, 297 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map
diff --git a/config/common_base b/config/common_base
index 4bcbaf9..682f8bf 100644
--- a/config/common_base
+++ b/config/common_base
@@ -716,6 +716,11 @@ CONFIG_RTE_LIBRTE_HASH=y
CONFIG_RTE_LIBRTE_HASH_DEBUG=n
#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+#
# Compile librte_efd
#
CONFIG_RTE_LIBRTE_EFD=y
diff --git a/lib/Makefile b/lib/Makefile
index afa604e..8cbd035 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index e114dcb..5929a34 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -498,6 +498,25 @@ enum rte_iova_mode rte_eal_iova_mode(void);
const char *
rte_eal_mbuf_user_pool_ops(void);
thanks for introducing this, I think this can be useful.
However, this deserves its own commit.
Post by Kevin Laatz
+typedef int (*rte_lib_init_fn)(void);
+
+typedef struct rte_lib_init_params {
This could be used to add arbitrary params, not only library init
functions.

This structure should be named "struct rte_param" instead (singular).
Post by Kevin Laatz
+ TAILQ_ENTRY(rte_lib_init_params) next;
+ char eal_flag[32];
+ char help_text[80];
You don't need to enforce length limit here. These structures will be
allocated statically, those two arrays could simply be pointers to
static strings.
Post by Kevin Laatz
+ rte_lib_init_fn lib_init;
Considering the more generic "rte_param" name,
"rte_param_cb cb;" might be more suited.
Post by Kevin Laatz
+ int enabled;
+} rte_lib_init_params;
+
+/**
+ *
This API is not EAL internal, it is public API.
Post by Kevin Laatz
+ * Structure containing the eal flag, the lib help string and the init
+ * function pointer for the library.
+ */
+void rte_lib_init_register(struct rte_lib_init_params *reg_init);
rte_param_register()
Post by Kevin Laatz
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index e59ac65..b9113c7 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -97,6 +97,13 @@ static char runtime_dir[PATH_MAX];
static const char *default_runtime_dir = "/var/run";
+TAILQ_HEAD(rte_lib_init_list, rte_lib_init_params);
+
+struct rte_lib_init_list rte_lib_init_list =
+ TAILQ_HEAD_INITIALIZER(rte_lib_init_list);
+
+rte_lib_init_params *lib_init_params;
+
You should not have these only in linuxapp env.
You need to add a compilation unit in
lib/librte_eal/common/ which will contain the list head and register
implementation, that will be linked from both linuxapp and bsdapp
targets.
Post by Kevin Laatz
int
eal_create_runtime_dir(void)
{
@@ -570,7 +577,7 @@ eal_log_level_parse(int argc, char **argv)
static int
eal_parse_args(int argc, char **argv)
{
- int opt, ret;
+ int opt, ret, valid_opt;
char **argvopt;
int option_index;
char *prgname = argv[0];
@@ -580,12 +587,27 @@ eal_parse_args(int argc, char **argv)
argvopt = argv;
optind = 1;
+ opterr = 0;
while ((opt = getopt_long(argc, argvopt, eal_short_options,
eal_long_options, &option_index)) != EOF) {
/* getopt is not happy, stop right now */
This comment should be at least rewritten, or removed.
Post by Kevin Laatz
if (opt == '?') {
+ valid_opt = 0;
+ /* Check if the flag is in the registered lib inits */
+ TAILQ_FOREACH(lib_init_params, &rte_lib_init_list, next) {
+ if (strcmp(argv[optind-1],
+ lib_init_params->eal_flag) == 0) {
+ lib_init_params->enabled = 1;
+ valid_opt = 1;
+ opterr = 0;
+ }
+ }
+
+ if (valid_opt)
+ continue;
+
A single helper function should be implemented,
"rte_param_parse()", that would return different codes for
(error | opt not found | opt found), and would be called here and
in bsdapp.
Post by Kevin Laatz
eal_usage(prgname);
ret = -1;
goto out;
@@ -786,6 +808,13 @@ static void rte_eal_init_alert(const char *msg)
RTE_LOG(ERR, EAL, "%s\n", msg);
}
+void
+rte_lib_init_register(struct rte_lib_init_params *reg_init)
+{
+ TAILQ_INSERT_HEAD(&rte_lib_init_list, reg_init, next);
+}
+
+
This should be in the common rte_param.c compilation unit.
Post by Kevin Laatz
/* Launch threads, called at application init(). */
int
rte_eal_init(int argc, char **argv)
@@ -1051,6 +1080,12 @@ rte_eal_init(int argc, char **argv)
rte_eal_mcfg_complete();
+ /* Call the init function for each registered and enabled lib */
+ TAILQ_FOREACH(lib_init_params, &rte_lib_init_list, next) {
+ if (lib_init_params->enabled)
+ lib_init_params->lib_init();
+ }
+
A helper "rte_param_init()" should be written instead, within which you
would call the parameter callback, but also check the return value and
stop init() on error.
Post by Kevin Laatz
return fctret;
}
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 344a43d..914d0fa 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -262,6 +262,13 @@ DPDK_18.08 {
} DPDK_18.05;
+DPDK_18.11 {
+
+ rte_lib_init_register;
+
+} DPDK_18.08;
+
EXPERIMENTAL {
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..0d61361
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+LDLIBS += -lpthread
+LDLIBS += -ljansson
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
new file mode 100644
index 0000000..d9ffec2
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = userdata;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_WARN("TELEMETRY could not be initialised");
+ return -1;
+ }
You have already dereferenced telemetry->thread_status in the caller,
this check will never trigger.

__rte_unused on userdata might be used while waiting for the actual
implementation to happen.
Post by Kevin Laatz
+
+ return 0;
+}
+
+static void
+*rte_telemetry_run_thread_func(void *userdata)
+{
+ int ret;
+ struct telemetry_impl *telemetry = userdata;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("%s passed a NULL instance", __func__);
+ pthread_exit(0);
+ }
You already checked the calloc return before spawning the thread, this
will never trigger.
Post by Kevin Laatz
+
+ 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);
+}
+
+int32_t
+rte_telemetry_init()
+{
+ int ret;
+ pthread_attr_t attr;
+ const char *telemetry_ctrl_thread = "telemetry";
The thread name is never re-used and its actual name is shorter than the
variable used to reference it, you might as well use the value itself
in the function call.
Post by Kevin Laatz
+
+ if (static_telemetry) {
+ TELEMETRY_LOG_WARN("TELEMETRY structure already initialised");
+ return -EALREADY;
+ }
+
+ static_telemetry = calloc(1, sizeof(struct telemetry_impl));
+ if (!static_telemetry) {
+ TELEMETRY_LOG_ERR("Memory could not be allocated");
+ return -ENOMEM;
+ }
+
+ static_telemetry->socket_id = rte_socket_id();
+ rte_metrics_init(static_telemetry->socket_id);
+ pthread_attr_init(&attr);
+ 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;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_register);
+
+static struct rte_lib_init_params lib_init_params = {
+ .eal_flag = "--telemetry",
+ .help_text = "Telemetry lib",
+ .lib_init = &rte_telemetry_init,
+ .enabled = 0
+};
+
+static void
+rte_telemetry_register(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+
+ rte_lib_init_register(&lib_init_params);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 0000000..f7ecb7b
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * Get the telemetry_impl structure device pointer initialised.
API description should not reference implementation details.
A pointer being initialized is the kind of information the function is
trying to abstract from the user, it's counter-productive to force it
back in the documentation.

A note telling that rte_telemetry, using rte_metrics, would initialize
the latter might be interesting.
Post by Kevin Laatz
+ *
+ * 0 on successful initialisation.
+ * -ENOMEM on memory allocation error
+ * -EPERM on unknown error failure
+ * -EALREADY if Telemetry is already initialised.
+ */
+int32_t
I think there have already been a remark on it, but why int32_t?
Errno is evaluated to an int type.
Post by Kevin Laatz
+rte_telemetry_init(void);
+
+/**
+ * Clean up and free memory.
+ *
+ * 0 on success
+ * -EPERM on failure
+ */
+int32_t
+rte_telemetry_cleanup(void);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
new file mode 100644
index 0000000..4e810a8
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <rte_log.h>
+
+#ifndef _RTE_TELEMETRY_INTERNAL_H_
+#define _RTE_TELEMETRY_INTERNAL_H_
+
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
volatile to disable possible optimization on the while loop?
Post by Kevin Laatz
+ uint32_t socket_id;
+} telemetry_impl;
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
new file mode 100644
index 0000000..992d227
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.11 {
+
+ rte_telemetry_init;
+ local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index eb91f10..fc84b2f 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -24,7 +24,7 @@ libraries = [ 'compat', # just a header, used for versioning
# add pkt framework libs which use other libs from above
'port', 'table', 'pipeline',
# flow_classify lib depends on pkt framework table lib
- 'flow_classify', 'bpf']
+ 'flow_classify', 'bpf', 'telemetry']
default_cflags = machine_args
if cc.has_argument('-Wno-format-truncation')
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index de33883..1223a85 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,6 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
--
Gaëtan Rivet
6WIND
Kevin Laatz
2018-10-03 17:36:04 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.

On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 210 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
2 files changed, 214 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index d9ffec2..0c99d66 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,22 +3,158 @@
*/

#include <unistd.h>
+#include <fcntl.h>
#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
+#include <rte_string_fns.h>

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

#define SLEEP_TIME 10

+#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+
+const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;

+int32_t
+rte_telemetry_is_port_active(int port_id)
+{
+ int ret;
+
+ ret = rte_eth_find_next(port_id);
+ if (ret == port_id)
+ return 1;
+
+ TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
+ port_id);
+ return 0;
+}
+
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
+{
+ int ret, num_xstats, ret_val, i;
+ struct rte_eth_xstat *eth_xstats = NULL;
+ struct rte_eth_xstat_name *eth_xstats_names = NULL;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
+ port_id, num_xstats);
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (!eth_xstats) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ return -ENOMEM;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ const char *xstats_names[num_xstats];
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ if (!eth_xstats_names) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats_names");
+ ret_val = -ENOMEM;
+ goto free_xstats;
+ }
+
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ ret_val = rte_metrics_reg_names(xstats_names, num_xstats);
+ if (ret_val < 0) {
+ TELEMETRY_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
+ ret_val = -1;
+ goto free_xstats;
+ }
+
+ goto free_xstats;
+
+free_xstats:
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return ret_val;
+}
+
+static int32_t
+rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
+{
+ int pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ telemetry->reg_index =
+ rte_telemetry_reg_ethdev_to_metrics(pid);
+ break;
+ }
+
+ if (telemetry->reg_index < 0) {
+ TELEMETRY_LOG_ERR("Failed to register ethdev metrics");
+ return -1;
+ }
+
+ telemetry->metrics_register_done = 1;
+
+ return 0;
+}
+
+static int32_t
+rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
+{
+ int ret;
+
+ if (telemetry->accept_fd == 0 || telemetry->accept_fd == -1) {
+ 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;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int32_t
rte_telemetry_run(void *userdata)
{
+ int ret;
struct telemetry_impl *telemetry = userdata;

if (!telemetry) {
@@ -26,6 +162,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_accept_new_client(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Accept and read new client failed");
+ return -1;
+ }
+
return 0;
}

@@ -49,6 +191,56 @@ static void
pthread_exit(0);
}

+static int32_t
+rte_telemetry_set_socket_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+
+ if (fd < 0) {
+ TELEMETRY_LOG_ERR("Invalid fd provided");
+ return -1;
+ }
+
+ 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 = {0};
+
+ if (!telemetry)
+ return -1;
+
+ telemetry->server_fd = socket(AF_UNIX, SOCK_STREAM, 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");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ 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");
+ return -1;
+ }
+
+ return 0;
+}
+
int32_t
rte_telemetry_init()
{
@@ -69,6 +261,14 @@ rte_telemetry_init()

static_telemetry->socket_id = rte_socket_id();
rte_metrics_init(static_telemetry->socket_id);
+ 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;
+ }
+
pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -88,11 +288,21 @@ rte_telemetry_init()
int32_t
rte_telemetry_cleanup(void)
{
+ int ret;
struct telemetry_impl *telemetry = static_telemetry;
+
+ 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;
}

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 4e810a8..569d56a 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -24,9 +24,13 @@ extern int telemetry_log_level;
TELEMETRY_LOG(INFO, fmt, ## args)

typedef struct telemetry_impl {
+ int accept_fd;
+ int server_fd;
pthread_t thread_id;
int thread_status;
uint32_t socket_id;
+ int reg_index;
+ int metrics_register_done;
} telemetry_impl;

#endif
--
2.9.5
Mattias Rönnblom
2018-10-03 18:40:06 UTC
Permalink
Post by Ciara Power
This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.
On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.
---
lib/librte_telemetry/rte_telemetry.c | 210 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
2 files changed, 214 insertions(+)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index d9ffec2..0c99d66 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,22 +3,158 @@
*/
#include <unistd.h>
+#include <fcntl.h>
#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
+#include <rte_string_fns.h>
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#define SLEEP_TIME 10
+#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+
FHS 3.0 recommends "/run" over "/var/run". Maybe a more descriptive
macro name would be in order, too.
Post by Ciara Power
+const char *socket_path = DEFAULT_DPDK_PATH;
Is there really a need for this variable? Why not use the macro directly?
Post by Ciara Power
static telemetry_impl *static_telemetry;
+int32_t
+rte_telemetry_is_port_active(int port_id)
+{
+ int ret;
+
+ ret = rte_eth_find_next(port_id);
+ if (ret == port_id)
+ return 1;
+
+ TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
+ port_id);
+ return 0;
+}
+
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
+{
+ int ret, num_xstats, ret_val, i;
+ struct rte_eth_xstat *eth_xstats = NULL;
+ struct rte_eth_xstat_name *eth_xstats_names = NULL;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
+ port_id, num_xstats);
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (!eth_xstats) {
eth_stats == NULL per 1.8.1 in the DPDK coding style guide.
Post by Ciara Power
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ return -ENOMEM;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ const char *xstats_names[num_xstats];
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ if (!eth_xstats_names) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats_names");
+ ret_val = -ENOMEM;
+ goto free_xstats;
+ }
+
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ ret_val = rte_metrics_reg_names(xstats_names, num_xstats);
+ if (ret_val < 0) {
+ TELEMETRY_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
+ ret_val = -1;
+ goto free_xstats;
+ }
+
+ goto free_xstats;
This goto seems a little redundant to me.
Post by Ciara Power
+
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return ret_val;
+}
+
+static int32_t
+rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
+{
+ int pid;
Ethernet port ids are uint16_t.
Post by Ciara Power
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ telemetry->reg_index =
+ rte_telemetry_reg_ethdev_to_metrics(pid);
+ break;
+ }
+
+ if (telemetry->reg_index < 0) {
+ TELEMETRY_LOG_ERR("Failed to register ethdev metrics");
+ return -1;
+ }
+
+ telemetry->metrics_register_done = 1;
+
+ return 0;
+}
+
+static int32_t
+rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
+{
+ int ret;
+
+ if (telemetry->accept_fd == 0 || telemetry->accept_fd == -1) {
+ 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 &&
accept() returns -1 on error. 0 is a valid fd (although in this case it
can't be returned, since at least one fd - the server socket - is open).
Post by Ciara Power
+ 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;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int32_t
rte_telemetry_run(void *userdata)
{
+ int ret;
struct telemetry_impl *telemetry = userdata;
if (!telemetry) {
1.8.1 again.
Post by Ciara Power
@@ -26,6 +162,12 @@ rte_telemetry_run(void *userdata)
return -1;
}
+ ret = rte_telemetry_accept_new_client(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Accept and read new client failed");
+ return -1;
+ }
+
return 0;
}
@@ -49,6 +191,56 @@ static void
pthread_exit(0);
}
+static int32_t
+rte_telemetry_set_socket_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+
+ if (fd < 0) {
+ TELEMETRY_LOG_ERR("Invalid fd provided");
+ return -1;
+ }
Shouldn't you do this check before you do the first fcntl()? If at all.
Post by Ciara Power
+
+ 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 = {0};
Aren't you planning to set all the relevant fields? No need to zero.
Post by Ciara Power
+
+ if (!telemetry)
1.8.1
Post by Ciara Power
+ return -1;
+
+ telemetry->server_fd = socket(AF_UNIX, SOCK_STREAM, 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");
Close the socket?
Post by Ciara Power
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ 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");
Close it here as well.
Post by Ciara Power
+ return -1;
+ }
+
+ return 0;
+}
+
int32_t
rte_telemetry_init()
{
@@ -69,6 +261,14 @@ rte_telemetry_init()
static_telemetry->socket_id = rte_socket_id();
rte_metrics_init(static_telemetry->socket_id);
+ 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;
+ }
+
pthread_attr_init(&attr);
Not a comment on this patch, but... technically, this call may fail.
Post by Ciara Power
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -88,11 +288,21 @@ rte_telemetry_init()
int32_t
rte_telemetry_cleanup(void)
{
+ int ret;
struct telemetry_impl *telemetry = static_telemetry;
+
+ 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;
}
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 4e810a8..569d56a 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -24,9 +24,13 @@ extern int telemetry_log_level;
TELEMETRY_LOG(INFO, fmt, ## args)
typedef struct telemetry_impl {
+ int accept_fd;
+ int server_fd;
pthread_t thread_id;
int thread_status;
uint32_t socket_id;
+ int reg_index;
+ int metrics_register_done;
} telemetry_impl;
#endif
Thomas Monjalon
2018-10-03 19:36:39 UTC
Permalink
Post by Mattias Rönnblom
Post by Ciara Power
+#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+
FHS 3.0 recommends "/run" over "/var/run". Maybe a more descriptive
macro name would be in order, too.
Kevin, you should use eal_get_runtime_dir().
You may need to export this function as a public API.

Mattias, thank you for the detailed review.
Mattias Rönnblom
2018-10-03 19:49:43 UTC
Permalink
Post by Thomas Monjalon
Post by Mattias Rönnblom
Post by Ciara Power
+#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+
FHS 3.0 recommends "/run" over "/var/run". Maybe a more descriptive
macro name would be in order, too.
Kevin, you should use eal_get_runtime_dir().
You may need to export this function as a public API.
You could also use the AF_UNIX abstract namespace. Convenient, as in you
don't have to bother with directories and dangling files from crashed
processes, but on the other hand it's Linux-specific and a little obscure.
Kevin Laatz
2018-10-03 17:36:05 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch introduces clients to the telemetry API.

When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.

A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.

Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/meson.build | 2 +
lib/librte_telemetry/rte_telemetry.c | 365 +++++++++++++++++++++++++-
lib/librte_telemetry/rte_telemetry_internal.h | 25 ++
mk/rte.app.mk | 2 +-
4 files changed, 390 insertions(+), 4 deletions(-)

diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076..0ccfa36 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_telemetry.c')
headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
+jansson = cc.find_library('jansson', required: true)
+ext_deps += jansson
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 0c99d66..7726fd4 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -7,6 +7,7 @@
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <jansson.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
@@ -16,6 +17,8 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

+#define BUF_SIZE 1024
+#define ACTION_POST 1
#define SLEEP_TIME 10

#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
@@ -34,6 +37,91 @@ rte_telemetry_is_port_active(int port_id)

TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
port_id);
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
+ const char *json_string)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Could not initialise TELEMETRY_API");
+ return -1;
+ }
+
+ if (!telemetry->request_client) {
+ TELEMETRY_LOG_ERR("No client has been chosen to write to");
+ return -1;
+ }
+
+ if (!json_string) {
+ 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) {
+ 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");
+ return -EPERM;
+ }
+
+ ret = json_object_set_new(root, "data", json_null());
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Data field cannot be set");
+ return -EPERM;
+ }
+
+ json_buffer = json_dumps(root, JSON_INDENT(2));
+ 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;
}

@@ -110,8 +198,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
int pid;

RTE_ETH_FOREACH_DEV(pid) {
- telemetry->reg_index =
- rte_telemetry_reg_ethdev_to_metrics(pid);
+ telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
break;
}

@@ -126,6 +213,33 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+ char buf[BUF_SIZE];
+ int ret, buffer_read = 0;
+ errno = 0;
+
+ buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+ buf[buffer_read] = '\0';
+
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Read error");
+ return -1;
+ } else if (buffer_read == 0) {
+ close(telemetry->accept_fd);
+ telemetry->accept_fd = 0;
+ } else {
+ ret = rte_telemetry_parse_client_message(telemetry, buf);
+ if (ret < 0)
+ TELEMETRY_LOG_WARN("Parse message failed");
+ close(telemetry->accept_fd);
+ telemetry->accept_fd = 0;
+ }
+
+ return 0;
+}
+
+static int32_t
rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
{
int ret;
@@ -136,8 +250,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
TELEMETRY_LOG_ERR("Listening error with server fd");
return -1;
}
- telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);

+ 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);
@@ -146,6 +260,31 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
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)
+{
+ 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);
+ client_buf[bytes] = '\0';
+
+ if (bytes > 0) {
+ telemetry->request_client = client;
+ }
}

return 0;
@@ -168,6 +307,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_read_client_sockets(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Client socket read failed");
+ return -1;
+ }
+
return 0;
}

@@ -268,6 +413,7 @@ rte_telemetry_init()
TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
return -EPERM;
}
+ TAILQ_INIT(&static_telemetry->client_list_head);

pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
@@ -285,11 +431,39 @@ rte_telemetry_init()
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) {
@@ -306,6 +480,191 @@ rte_telemetry_cleanup(void)
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) {
+ TELEMETRY_LOG_WARN("TELEMETRY is not initialised");
+ return -ENODEV;
+ }
+
+ if (!client_path) {
+ 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 = {0};
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+ return -ENODEV;
+ }
+
+ if (!client_path) {
+ 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_STREAM, 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 =
+ (telemetry_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) {
+ 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");
+ json_decref(root);
+ goto fail;
+ }
+
+ json_t *action = json_object_get(root, "action");
+ if (!action) {
+ 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) {
+ 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) {
+ TELEMETRY_LOG_WARN("Request does not have data field");
+ goto fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
+ if (!client_path) {
+ 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");
+ return -1;
+}
+
int telemetry_log_level;
RTE_INIT(rte_telemetry_register);

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 569d56a..e3292cf 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -3,6 +3,7 @@
*/

#include <rte_log.h>
+#include <rte_tailq.h>

#ifndef _RTE_TELEMETRY_INTERNAL_H_
#define _RTE_TELEMETRY_INTERNAL_H_
@@ -23,6 +24,12 @@ extern int telemetry_log_level;
#define TELEMETRY_LOG_INFO(fmt, args...) \
TELEMETRY_LOG(INFO, fmt, ## args)

+typedef struct telemetry_client {
+ char *file_path;
+ int fd;
+ TAILQ_ENTRY(telemetry_client) client_list;
+} telemetry_client;
+
typedef struct telemetry_impl {
int accept_fd;
int server_fd;
@@ -31,6 +38,24 @@ typedef struct telemetry_impl {
uint32_t socket_id;
int reg_index;
int metrics_register_done;
+ TAILQ_HEAD(, telemetry_client) client_list_head;
+ struct telemetry_client *request_client;
+ int register_fail_count;
} telemetry_impl;

+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);
+
+int32_t
+rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
+ int error_type);
+
+int32_t
+rte_telemetry_register_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
+int32_t
+rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
#endif
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 1223a85..26c3f22 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
-_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry -ljansson
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
Mattias Rönnblom
2018-10-03 19:06:53 UTC
Permalink
Post by Ciara Power
This patch introduces clients to the telemetry API.
When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.
A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.
Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.
---
lib/librte_telemetry/meson.build | 2 +
lib/librte_telemetry/rte_telemetry.c | 365 +++++++++++++++++++++++++-
lib/librte_telemetry/rte_telemetry_internal.h | 25 ++
mk/rte.app.mk | 2 +-
4 files changed, 390 insertions(+), 4 deletions(-)
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076..0ccfa36 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_telemetry.c')
headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
+jansson = cc.find_library('jansson', required: true)
+ext_deps += jansson
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 0c99d66..7726fd4 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -7,6 +7,7 @@
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <jansson.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
@@ -16,6 +17,8 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
+#define BUF_SIZE 1024
+#define ACTION_POST 1
#define SLEEP_TIME 10
#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
@@ -34,6 +37,91 @@ rte_telemetry_is_port_active(int port_id)
TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
port_id);
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
+ const char *json_string)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Could not initialise TELEMETRY_API");
+ return -1;
+ }
1.8.1 here again, and in many instances below.
Post by Ciara Power
+
+ if (!telemetry->request_client) {
+ TELEMETRY_LOG_ERR("No client has been chosen to write to");
+ return -1;
+ } > +
+ if (!json_string) {
+ TELEMETRY_LOG_ERR("Invalid JSON string!");
+ return -1;
+ }
+
+ ret = send(telemetry->request_client->fd,
+ json_string, strlen(json_string), 0);
How would this code handle a partial success (as in, for example, half
of the string fits the socket buffer)? In not, maybe switching over to a
SOCK_SEQPACKET AF_UNIX socket would be the best way around it.
Post by Ciara Power
+ 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) {
+ 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");
No json_decref()?
Post by Ciara Power
+ return -EPERM;
+ }
+
+ ret = json_object_set_new(root, "data", json_null());
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Data field cannot be set");
... and a json_decref() here too?
Post by Ciara Power
+ return -EPERM;
+ }
+
+ json_buffer = json_dumps(root, JSON_INDENT(2));
+ 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;
}
@@ -110,8 +198,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
int pid;
RTE_ETH_FOREACH_DEV(pid) {
- telemetry->reg_index =
- rte_telemetry_reg_ethdev_to_metrics(pid);
+ telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
break;
}
@@ -126,6 +213,33 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}
static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+ char buf[BUF_SIZE];
+ int ret, buffer_read = 0;
+ errno = 0;
Generally speaking, you don't touch errno unless your function fails. In
this case, I don't see the point at all.
Post by Ciara Power
+
+ buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
This and the below code seem to assume that read() returns one and only
one message, but on a SOCK_STREAM, there is no such thing as a message.
It's a byte stream, and you need to provide your own framing, or have an
application protocol which allows only have one outstanding request. If
you do the latter, you still need to allow for "short" (partial) read()s
(i.e. re-read() until done).
Post by Ciara Power
+ buf[buffer_read] = '\0';
If buffer_read == -1, the above isn't a good idea.
Post by Ciara Power
+
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Read error");
+ return -1;
+ } else if (buffer_read == 0) {
+ close(telemetry->accept_fd);
+ telemetry->accept_fd = 0;
+ } else {
+ ret = rte_telemetry_parse_client_message(telemetry, buf);
+ if (ret < 0)
+ TELEMETRY_LOG_WARN("Parse message failed");
+ close(telemetry->accept_fd);
+ telemetry->accept_fd = 0;
Maybe put the cleanup actions behind a goto label?
Post by Ciara Power
+ }
+
+ return 0;
+}
+
+static int32_t
rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
{
int ret;
@@ -136,8 +250,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
TELEMETRY_LOG_ERR("Listening error with server fd");
return -1;
}
- telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+ 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);
@@ -146,6 +260,31 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
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)
+{
+ 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);
+ client_buf[bytes] = '\0';
+
+ if (bytes > 0) {
+ telemetry->request_client = client;
+ }
}
return 0;
@@ -168,6 +307,12 @@ rte_telemetry_run(void *userdata)
return -1;
}
+ ret = rte_telemetry_read_client_sockets(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Client socket read failed");
+ return -1;
+ }
+
return 0;
}
@@ -268,6 +413,7 @@ rte_telemetry_init()
TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
return -EPERM;
}
+ TAILQ_INIT(&static_telemetry->client_list_head);
pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
@@ -285,11 +431,39 @@ rte_telemetry_init()
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;
There are other close() calls for which the return code is not checked.
The code should be consistent, one way or the other.
Post by Ciara Power
+ }
+
+ 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) {
@@ -306,6 +480,191 @@ rte_telemetry_cleanup(void)
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) {
+ TELEMETRY_LOG_WARN("TELEMETRY is not initialised");
+ return -ENODEV;
+ }
+
+ if (!client_path) {
+ 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;
+
+ 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 = {0};
Setting all the fields, so no need to clear the struct.
Post by Ciara Power
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+ return -ENODEV;
+ }
+
+ if (!client_path) {
+ 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_STREAM, 0);
+ if (fd == -1) {
+ TELEMETRY_LOG_ERR("Client socket error");
+ return -EACCES;
+ }
+ ret = rte_telemetry_set_socket_nonblock(fd);
You could also use SOCK_NONBLOCK at the time of the socket() call.
Post by Ciara Power
+ 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 =
+ (telemetry_client *)malloc(sizeof(telemetry_client));
malloc() returns a void pointer, so no need to cast.
Kevin Laatz
2018-10-03 17:36:06 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the parser file. This is used to parse any
messages that are received on any of the client sockets.

Currently, the unregister functionality works using the parser.
Functionality relating to getting statistic values for certain ports
will be added in a subsequent patch, however the parsing involved
for that command is added in this patch.

Some of the parser code included is in preparation for future
functionality, that is not implemented yet in this patchset.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 8 +
lib/librte_telemetry/rte_telemetry_internal.h | 13 +
lib/librte_telemetry/rte_telemetry_parser.c | 569 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
6 files changed, 606 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index 0d61361..95c7296 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -21,6 +21,7 @@ LIBABIVER := 1

# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 0ccfa36..7450f96 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 7726fd4..2e879b7 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -16,6 +16,7 @@

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
+#include "rte_telemetry_parser.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
@@ -274,6 +275,7 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
static int32_t
rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
{
+ int ret;
telemetry_client *client;
char client_buf[BUF_SIZE];
int bytes;
@@ -284,6 +286,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)

if (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;
+ }
}
}

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf..86a5ba1 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -43,6 +43,11 @@ typedef struct telemetry_impl {
int register_fail_count;
} telemetry_impl;

+enum rte_telemetry_parser_actions {
+ ACTION_GET = 0,
+ ACTION_DELETE = 2
+};
+
int32_t
rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);

@@ -58,4 +63,12 @@ int32_t
rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
const char *client_path);

+/**
+ * This is a wrapper for the ethdev api rte_eth_find_next().
+ * If rte_eth_find_next() returns the same port id that we passed it,
+ * then we know that that port is active.
+ */
+int32_t
+rte_telemetry_is_port_active(int port_id);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
new file mode 100644
index 0000000..d2065f4
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -0,0 +1,569 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+
+#include <rte_metrics.h>
+#include <rte_common.h>
+#include <rte_ethdev.h>
+
+#include "rte_telemetry_internal.h"
+
+typedef int (*command_func)(struct telemetry_impl *, int, json_t *);
+
+struct rte_telemetry_command {
+ char *text;
+ command_func fn;
+} command;
+
+static int32_t
+rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ goto einval_fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
+ if (!json_is_string(client_path)) {
+ TELEMETRY_LOG_WARN("Command value is not a string");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_unregister_client(telemetry,
+ json_string_value(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not unregister client");
+ goto einval_fail;
+ }
+
+ 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_command_ports(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'ports' command");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ 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_command_ports_details(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ json_t *value, *port_ids_json = json_object_get(data, "ports");
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ int ret, port_ids[num_port_ids];
+ RTE_SET_USED(port_ids);
+ size_t index;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_array(port_ids_json)) {
+ TELEMETRY_LOG_WARN("Invalid Port ID array");
+ goto einval_fail;
+ }
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Port ID given is invalid");
+ goto einval_fail;
+ }
+ port_ids[index] = json_integer_value(value);
+ }
+
+ 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_command_port_stats(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'port_stats' command");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ 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_stat_names_to_ids(struct telemetry_impl *telemetry,
+ const char * const *stat_names, uint32_t *stat_ids,
+ uint64_t num_stat_names)
+{
+ struct rte_metric_name *names;
+ int ret, num_metrics;
+ uint32_t i, k;
+
+ if (!stat_names) {
+ TELEMETRY_LOG_WARN("Invalid stat_names argument");
+ goto einval_fail;
+ }
+
+ if (num_stat_names <= 0) {
+ TELEMETRY_LOG_WARN("Invalid num_stat_names argument");
+ goto einval_fail;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+ goto eperm_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_WARN("No metrics have been registered");
+ goto eperm_fail;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (!names) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory for names");
+
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Cannot get metrics names");
+ free(names);
+ goto eperm_fail;
+ }
+
+ k = 0;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ uint32_t j;
+ for (j = 0; j < (uint32_t)num_metrics; j++) {
+ if (strcmp(stat_names[i], names[j].name) == 0) {
+ stat_ids[k] = j;
+ k++;
+ break;
+ }
+ }
+ }
+
+ if (k != num_stat_names) {
+ TELEMETRY_LOG_WARN("Invalid stat names provided");
+ free(names);
+ goto einval_fail;
+ }
+
+ free(names);
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret, num_metrics, i, p;
+ struct rte_metric_name *names;
+ uint64_t num_port_ids = 0;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (!names) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ const char *stat_names[num_metrics];
+ uint32_t stat_ids[num_metrics];
+
+ RTE_ETH_FOREACH_DEV(p) {
+ num_port_ids++;
+ }
+
+ if (!num_port_ids) {
+ TELEMETRY_LOG_WARN("No active ports");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ goto fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ for (i = 0; i < num_metrics; i++)
+ stat_names[i] = names[i].name;
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_metrics);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ free(names);
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
+ *telemetry, int action, json_t *data)
+{
+ int ret;
+ json_t *port_ids_json = json_object_get(data, "ports");
+ json_t *stat_names_json = json_object_get(data, "stats");
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ uint64_t num_stat_names = json_array_size(stat_names_json);
+ const char *stat_names[num_stat_names];
+ uint32_t port_ids[num_port_ids], stat_ids[num_stat_names];
+ size_t index;
+ json_t *value;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (!json_is_array(port_ids_json) ||
+ !json_is_array(stat_names_json)) {
+ TELEMETRY_LOG_WARN("Invalid input data array(s)");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Port ID given is not valid");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ ret = rte_telemetry_is_port_active(port_ids[index]);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+ }
+
+ json_array_foreach(stat_names_json, index, value) {
+ if (!json_is_string(value)) {
+ TELEMETRY_LOG_WARN("Stat Name given is not a string");
+
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+ stat_names[index] = json_string_value(value);
+ }
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_stat_names);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
+ return -1;
+ }
+ return 0;
+}
+
+static int32_t
+rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
+ const char *command, json_t *data)
+{
+ int ret;
+ uint32_t i;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ struct rte_telemetry_command commands[] = {
+ {
+ .text = "clients",
+ .fn = &rte_telemetry_command_clients
+ },
+ {
+ .text = "ports",
+ .fn = &rte_telemetry_command_ports
+ },
+ {
+ .text = "ports_details",
+ .fn = &rte_telemetry_command_ports_details
+ },
+ {
+ .text = "port_stats",
+ .fn = &rte_telemetry_command_port_stats
+ },
+ {
+ .text = "ports_stats_values_by_name",
+ .fn = &rte_telemetry_command_ports_stats_values_by_name
+ },
+ {
+ .text = "ports_all_stat_values",
+ .fn = &rte_telemetry_command_ports_all_stat_values
+ }
+ };
+
+ const uint32_t num_commands = RTE_DIM(command);
+
+ for (i = 0; i < num_commands; i++) {
+ if (strcmp(command, commands[i].text) == 0) {
+ ret = commands[i].fn(telemetry, action, data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Command Function for %s failed",
+ commands[i].text);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ TELEMETRY_LOG_WARN("\"%s\" command not found", command);
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+}
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
+{
+ int ret, action_int;
+ json_error_t error;
+ json_t *root, *action, *command, *data;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ root = json_loads(socket_rx_data, 0, &error);
+ if (!root) {
+ TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+ error.text);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -EPERM;
+ } else if (!json_is_object(root)) {
+ TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+ json_decref(root);
+ goto einval_fail;
+ }
+
+ action = json_object_get(root, "action");
+ if (!action) {
+ TELEMETRY_LOG_WARN("Request does not have action field");
+ goto einval_fail;
+ } else if (!json_is_integer(action)) {
+ TELEMETRY_LOG_WARN("Action value is not an integer");
+ goto einval_fail;
+ }
+
+ command = json_object_get(root, "command");
+ if (!command) {
+ TELEMETRY_LOG_WARN("Request does not have command field");
+ goto einval_fail;
+ } else if (!json_is_string(command)) {
+ TELEMETRY_LOG_WARN("Command value is not a string");
+ goto einval_fail;
+ }
+
+ action_int = json_integer_value(action);
+ if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Invalid action code");
+ goto einval_fail;
+ }
+
+ const char *command_string = json_string_value(command);
+ data = json_object_get(root, "data");
+ if (!data) {
+ TELEMETRY_LOG_WARN("Request does not have data field");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
+ data);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse command");
+ return -EINVAL;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -EPERM;
+ }
+ return -EINVAL;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser.h b/lib/librte_telemetry/rte_telemetry_parser.h
new file mode 100644
index 0000000..63e633d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_PARSER_H_
+#define _RTE_TELEMETRY_PARSER_H_
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data);
+
+#endif
--
2.9.5
Kevin Laatz
2018-10-03 17:36:07 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to update the statistics in
the metrics library with values from the ethdev stats.

Values need to be updated before they are encoded into a JSON
message and sent to the client that requested them. The JSON encoding
will be added in a subsequent patch.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 134 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
lib/librte_telemetry/rte_telemetry_parser.c | 17 ++++
3 files changed, 155 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 2e879b7..596c611 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -42,6 +42,78 @@ rte_telemetry_is_port_active(int port_id)
return 0;
}

+static int32_t
+rte_telemetry_update_metrics_ethdev(struct telemetry_impl *telemetry,
+ uint16_t port_id, int reg_start_index)
+{
+ int ret, num_xstats, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_telemetry_is_port_active(port_id);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
+ num_xstats);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (!eth_xstats) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ uint64_t xstats_values[num_xstats];
+ for (i = 0; i < num_xstats; i++)
+ xstats_values[i] = eth_xstats[i].value;
+
+ ret = rte_metrics_update_values(port_id, reg_start_index, xstats_values,
+ num_xstats);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not update metrics values");
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ free(eth_xstats);
+ return -1;
+ }
+
+ free(eth_xstats);
+ return 0;
+}
+
int32_t
rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
const char *json_string)
@@ -126,6 +198,68 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
+{
+ int ret, i;
+ char *json_buffer = NULL;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!metric_ids) {
+ TELEMETRY_LOG_ERR("Invalid metric_ids array");
+ goto einval_fail;
+ }
+
+ if (num_metric_ids < 0) {
+ TELEMETRY_LOG_ERR("Invalid num_metric_ids, must be positive");
+ goto einval_fail;
+ }
+
+ if (!port_ids) {
+ TELEMETRY_LOG_ERR("Invalid port_ids array");
+ goto einval_fail;
+ }
+
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Invalid num_port_ids, must be positive");
+ goto einval_fail;
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]);
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_update_metrics_ethdev(telemetry,
+ port_ids[i], telemetry->reg_index);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Failed to update ethdev metrics");
+ 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_reg_ethdev_to_metrics(uint16_t port_id)
{
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 86a5ba1..0082cb2 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -71,4 +71,8 @@ rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
int32_t
rte_telemetry_is_port_active(int port_id);

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
index d2065f4..7f1754c 100644
--- a/lib/librte_telemetry/rte_telemetry_parser.c
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -258,6 +258,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
int ret, num_metrics, i, p;
struct rte_metric_name *names;
uint64_t num_port_ids = 0;
+ uint32_t port_ids[RTE_MAX_ETHPORTS];

if (!telemetry) {
TELEMETRY_LOG_ERR("Invalid telemetry argument");
@@ -313,6 +314,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
uint32_t stat_ids[num_metrics];

RTE_ETH_FOREACH_DEV(p) {
+ port_ids[num_port_ids] = p;
num_port_ids++;
}

@@ -337,6 +339,13 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
goto fail;
}

+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_metrics,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Sending ports stats values failed");
+ goto fail;
+ }
+
return 0;

fail:
@@ -428,6 +437,14 @@ rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
return -1;
}
+
+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Sending ports stats values failed");
+ return -1;
+ }
+
return 0;
}
--
2.9.5
Kevin Laatz
2018-10-03 17:36:08 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to create a JSON message in
order to send it to a client socket.

When stats are requested by a client, they are retrieved from
the metrics library and encoded in JSON format.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 310 ++++++++++++++++++++++++++++++++++-
1 file changed, 308 insertions(+), 2 deletions(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 596c611..5b1d2ef 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -186,7 +186,7 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return -EPERM;
}

- json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_buffer = json_dumps(root, 0);
json_decref(root);

ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
@@ -198,6 +198,304 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+static int
+rte_telemetry_get_metrics(struct telemetry_impl *telemetry, uint32_t port_id,
+ struct rte_metric_value *metrics, struct rte_metric_name *names,
+ int num_metrics)
+{
+ int ret, num_values;
+
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Invalid metrics count");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+ goto eperm_fail;
+ }
+
+ if (!metrics) {
+ TELEMETRY_LOG_ERR("Metrics must be initialised.");
+ goto einval_fail;
+ }
+
+ if (!names) {
+ TELEMETRY_LOG_ERR("Names must be initialised.");
+ goto einval_fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Cannot get metrics names");
+ goto eperm_fail;
+ }
+
+ num_values = rte_metrics_get_values(port_id, NULL, 0);
+ ret = rte_metrics_get_values(port_id, metrics, num_values);
+ if (ret < 0 || ret > num_values) {
+ TELEMETRY_LOG_ERR("Cannot get metrics values");
+ goto eperm_fail;
+ }
+
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+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_json_format_stat(struct telemetry_impl *telemetry, json_t *stats,
+ const char *metric_name, uint64_t metric_value)
+{
+ int ret;
+ json_t *stat = json_object();
+
+ if (!stat) {
+ TELEMETRY_LOG_ERR("Could not create stat JSON object");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "name", json_string(metric_name));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat Name field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "value", json_integer(metric_value));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat Value field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(stats, stat);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat cannot be added to stats json array");
+ goto eperm_fail;
+ }
+
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+}
+
+static int32_t
+rte_telemetry_json_format_port(struct telemetry_impl *telemetry,
+ uint32_t port_id, json_t *ports, uint32_t *metric_ids,
+ uint32_t num_metric_ids)
+{
+ struct rte_metric_value *metrics = 0;
+ struct rte_metric_name *names = 0;
+ int num_metrics, ret, err_ret;
+ json_t *port, *stats;
+ uint32_t i;
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+ goto eperm_fail;
+ }
+
+ metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (!metrics || !names) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory");
+ free(metrics);
+ free(names);
+
+ err_ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (err_ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_telemetry_get_metrics(telemetry, port_id, metrics, names,
+ num_metrics);
+ if (ret < 0) {
+ free(metrics);
+ free(names);
+ TELEMETRY_LOG_ERR("rte_telemetry_get_metrics failed");
+ return -1;
+ }
+
+ port = json_object();
+ stats = json_array();
+ if (!port || !stats) {
+ TELEMETRY_LOG_ERR("Could not create port/stats JSON objects");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(port, "port", json_integer(port_id));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Port field cannot be set");
+ goto eperm_fail;
+ }
+
+ for (i = 0; i < num_metric_ids; i++) {
+ int metric_id = metric_ids[i];
+ int metric_index = -1;
+ int metric_name_key = -1;
+ int32_t j;
+ uint64_t metric_value;
+
+ if (metric_id >= num_metrics) {
+ TELEMETRY_LOG_ERR("Metric_id: %d is not valid",
+ metric_id);
+ goto einval_fail;
+ }
+
+ for (j = 0; j < num_metrics; j++) {
+ if (metrics[j].key == metric_id) {
+ metric_name_key = metrics[j].key;
+ metric_index = j;
+ break;
+ }
+ }
+
+ const char *metric_name = names[metric_name_key].name;
+ metric_value = metrics[metric_index].value;
+
+ if (metric_name_key < 0 || metric_index < 0) {
+ TELEMETRY_LOG_ERR("Could not get metric name/index");
+ goto eperm_fail;
+ }
+
+ ret = rte_telemetry_json_format_stat(telemetry, stats,
+ metric_name, metric_value);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Format stat with id: %u failed",
+ metric_id);
+ free(metrics);
+ free(names);
+ return -1;
+ }
+ }
+
+ if (json_array_size(stats) == 0)
+ ret = json_object_set_new(port, "stats", json_null());
+ else
+ ret = json_object_set_new(port, "stats", stats);
+
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stats object cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(ports, port);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Port object cannot be added to ports array");
+ goto eperm_fail;
+ }
+
+ free(metrics);
+ free(names);
+ return 0;
+
+eperm_fail:
+ free(metrics);
+ free(names);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+einval_fail:
+ free(metrics);
+ free(names);
+ 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_encode_json_format(struct telemetry_impl *telemetry,
+ uint32_t *port_ids, uint32_t num_port_ids, uint32_t *metric_ids,
+ uint32_t num_metric_ids, char **json_buffer)
+{
+ int ret;
+ json_t *root, *ports;
+ uint32_t i;
+
+ if (num_port_ids <= 0 || num_metric_ids <= 0) {
+ TELEMETRY_LOG_ERR("Please provide port and metric ids to query");
+ goto einval_fail;
+ }
+
+ ports = json_array();
+ if (!ports) {
+ TELEMETRY_LOG_ERR("Could not create ports JSON array");
+ goto eperm_fail;
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]);
+ goto einval_fail;
+ }
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ ret = rte_telemetry_json_format_port(telemetry, port_ids[i],
+ ports, metric_ids, num_metric_ids);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Format port in JSON failed");
+ return -1;
+ }
+ }
+
+ root = json_object();
+ if (!root) {
+ TELEMETRY_LOG_ERR("Could not create root JSON object");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "status_code",
+ json_string("Status OK: 200"));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Status code field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "data", ports);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Data field cannot be set");
+ goto eperm_fail;
+ }
+
+ *json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_decref(root);
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+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(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
@@ -237,13 +535,20 @@ rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
}

ret = rte_telemetry_update_metrics_ethdev(telemetry,
- port_ids[i], telemetry->reg_index);
+ port_ids[i], telemetry->reg_index);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to update ethdev metrics");
return -1;
}
}

+ ret = rte_telemetry_encode_json_format(telemetry, port_ids,
+ num_port_ids, metric_ids, num_metric_ids, &json_buffer);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON encode function failed");
+ return -1;
+ }
+
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not write to socket");
@@ -367,6 +672,7 @@ rte_telemetry_read_client(struct telemetry_impl *telemetry)
ret = rte_telemetry_parse_client_message(telemetry, buf);
if (ret < 0)
TELEMETRY_LOG_WARN("Parse message failed");
+
close(telemetry->accept_fd);
telemetry->accept_fd = 0;
}
--
2.9.5
Kevin Laatz
2018-10-03 17:36:09 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds all tests for the Telemetry API.
The tests added include a parser test, selftest, and socket
messaging tests.

The parser tests pass valid and invalid messages to the parser
to ensure the correct return values are received.
The selftest tests basic functions in the Telemetry API such as
registering, unregistering, and initialisation.
The socket messaging tests pass messages through the socket and
validates the return message, to ensure the Telemetry API is
responding correctly.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 651 +++++++++++++++++++++-
lib/librte_telemetry/rte_telemetry.h | 12 +
lib/librte_telemetry/rte_telemetry_internal.h | 3 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 534 ++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 ++
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 ++
lib/librte_telemetry/rte_telemetry_version.map | 1 +
9 files changed, 1278 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index 95c7296..1a05069 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -22,6 +22,7 @@ LIBABIVER := 1
# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser_test.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7450f96..57dd83d 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c', 'rte_telemetry_parser_test.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h', 'rte_telemetry_parser_test.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 5b1d2ef..43f7a93 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -17,16 +17,34 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#include "rte_telemetry_parser.h"
+#include "rte_telemetry_parser_test.h"
+#include "rte_telemetry_socket_tests.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
#define SLEEP_TIME 10

#define DEFAULT_DPDK_PATH "/var/run/.rte_telemetry"
+#define SELFTEST_VALID_CLIENT "/var/run/valid_client"
+#define SELFTEST_INVALID_CLIENT "/var/run/invalid_client"
+#define SOCKET_TEST_CLIENT_PATH "/var/run/client"

const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;

+struct telemetry_message_test {
+ char *test_name;
+ int (*test_func_ptr)(struct telemetry_impl *telemetry, int fd);
+};
+
+struct json_data {
+ char *status_code;
+ char *data;
+ int port;
+ char *stat_name;
+ int stat_value;
+};
+
int32_t
rte_telemetry_is_port_active(int port_id)
{
@@ -635,7 +653,7 @@ rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
static int32_t
rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
- int pid;
+ int pid, ret;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
@@ -648,6 +666,18 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

telemetry->metrics_register_done = 1;
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ 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;
}
@@ -1113,6 +1143,625 @@ rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
return -1;
}

+int32_t
+rte_telemetry_dummy_client_socket(const char *valid_client_path)
+{
+ int sockfd = socket(AF_UNIX, SOCK_STREAM, 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) {
+ TELEMETRY_LOG_ERR("Could not initialize Telemetry API");
+ return -1;
+ }
+
+ telemetry->server_fd = socket;
+ telemetry->reg_index = 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;
+ struct sockaddr_un addr = {0};
+ struct sockaddr_un addrs = {0};
+ *send_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ *recv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ listen(telemetry->server_fd, 5);
+ addr.sun_family = AF_UNIX;
+ 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) {
+ TELEMETRY_LOG_ERR("JSON message is NULL");
+ return -EINVAL;
+ }
+
+ if (!root) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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;
+ char *status = "Status Error: Invalid Argument 404";
+ 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";
+ 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;
RTE_INIT(rte_telemetry_register);

diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
index f7ecb7b..975c305 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -33,4 +33,16 @@ rte_telemetry_init(void);
int32_t
rte_telemetry_cleanup(void);

+/**
+ * Runs various tests to ensure telemetry initialisation and register/unregister
+ * functions are working correctly.
+ *
+ * @return
+ * 0 on success when all tests have passed
+ * @return
+ * -1 on failure when the test has failed
+ */
+int32_t
+rte_telemetry_selftest(void);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 0082cb2..de7afda 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -75,4 +75,7 @@ int32_t
rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);

+int32_t
+rte_telemetry_socket_messaging_testing(int index, int socket);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.c b/lib/librte_telemetry/rte_telemetry_parser_test.c
new file mode 100644
index 0000000..13a9ca8
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.c
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_tailq.h>
+#include <rte_string_fns.h>
+
+#include "rte_telemetry_parser.h"
+
+enum choices {
+ INV_ACTION_VAL,
+ INV_COMMAND_VAL,
+ INV_DATA_VAL,
+ INV_ACTION_FIELD,
+ INV_COMMAND_FIELD,
+ INV_DATA_FIELD,
+ INV_JSON_FORMAT,
+ VALID_REQ
+};
+
+
+#define TEST_CLIENT "/var/run/test_client"
+
+int32_t
+rte_telemetry_create_test_socket(struct telemetry_impl *telemetry,
+ const char *test_client_path)
+{
+ int ret, sockfd;
+ struct sockaddr_un addr = {0};
+ struct telemetry_client *client;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ TELEMETRY_LOG_ERR("Test socket creation failure");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, test_client_path, sizeof(addr.sun_path));
+ unlink(test_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;
+ }
+
+ ret = rte_telemetry_register_client(telemetry, test_client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Register dummy client failed: %i", ret);
+ return -1;
+ }
+
+ ret = accept(sockfd, NULL, NULL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Socket accept failed");
+ return -1;
+ }
+
+ TAILQ_FOREACH(client, &telemetry->client_list_head, client_list)
+ telemetry->request_client = client;
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, json_t **data)
+{
+
+ int ret;
+ json_t *stat_names_json_array = NULL;
+ json_t *port_ids_json_array = NULL;
+ uint32_t i;
+
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Port Ids Count invalid");
+ goto fail;
+ }
+
+ *data = json_object();
+ if (!*data) {
+ TELEMETRY_LOG_ERR("Data json object creation failed");
+ goto fail;
+ }
+
+ port_ids_json_array = json_array();
+ if (!port_ids_json_array) {
+ TELEMETRY_LOG_ERR("port_ids_json_array creation failed");
+ goto fail;
+ }
+
+ for (i = 0; i < (uint32_t)num_port_ids; i++) {
+ ret = json_array_append(port_ids_json_array,
+ json_integer(port_ids[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON array creation failed");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "ports", port_ids_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting 'ports' value in data object failed");
+ goto fail;
+ }
+
+ if (stat_names) {
+ if (num_stat_names < 0) {
+ TELEMETRY_LOG_ERR("Stat Names Count invalid");
+ goto fail;
+ }
+
+ stat_names_json_array = json_array();
+ if (!stat_names_json_array) {
+ TELEMETRY_LOG_ERR("stat_names_json_array creation failed");
+ goto fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ ret = json_array_append(stat_names_json_array,
+ json_string(stat_names[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON array creation failed");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "stats", stat_names_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting 'stats' value in data object failed");
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ if (*data)
+ json_decref(*data);
+ if (stat_names_json_array)
+ json_decref(stat_names_json_array);
+ if (port_ids_json_array)
+ json_decref(port_ids_json_array);
+ return -1;
+}
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, char **request,
+ int inv_choice)
+{
+ int ret;
+ json_t *root = json_object();
+ json_t *data;
+
+ if (!root) {
+ TELEMETRY_LOG_ERR("Could not create root json object");
+ goto fail;
+ }
+
+ if (inv_choice == INV_ACTION_FIELD) {
+ ret = json_object_set_new(root, "ac--on", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid action field in root object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "action", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid action field in root object failed");
+ goto fail;
+ }
+ }
+
+ if (inv_choice == INV_COMMAND_FIELD) {
+ ret = json_object_set_new(root, "co---nd", json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid command field in root object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "command", json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid command field in root object failed");
+ goto fail;
+ }
+ }
+
+ data = json_null();
+ if (client_path) {
+ data = json_object();
+ if (!data) {
+ TELEMETRY_LOG_ERR("Data json object creation failed");
+ goto fail;
+ }
+
+ ret = json_object_set_new(data, "client_path",
+ json_string(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid client_path field in data object failed");
+ goto fail;
+ }
+
+ } else if (port_ids) {
+ ret = rte_telemetry_format_port_stat_ids(port_ids, num_port_ids,
+ stat_names, num_stat_names, &data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Formatting Port/Stat arrays failed");
+ goto fail;
+ }
+
+ }
+
+ if (inv_choice == INV_DATA_FIELD) {
+ ret = json_object_set_new(root, "d--a", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid data field in data object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "data", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid data field in data object failed");
+ goto fail;
+ }
+ }
+
+ *request = json_dumps(root, 0);
+ if (!*request) {
+ TELEMETRY_LOG_ERR("Converting JSON root object to char* failed");
+ goto fail;
+ }
+
+ json_decref(root);
+ return 0;
+
+fail:
+ if (root)
+ json_decref(root);
+ return -1;
+}
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice)
+{
+ int ret;
+ char *request;
+ char *client_path_data = NULL;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command_choice = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path_data = "INVALID_DATA";
+
+ ret = rte_telemetry_create_json_request(action_choice, command_choice,
+ client_path_data, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice)
+{
+ int ret;
+ char *request;
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ char *command = "ports_details";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ port_ids = NULL;
+
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names,
+ int inv_choice)
+{
+ int ret;
+ char *request;
+ char *command = "ports_stats_values_by_name";
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL) {
+ port_ids = NULL;
+ stat_names = NULL;
+ }
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, stat_names, num_stat_names, &request,
+ inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_unreg_request(struct telemetry_impl *telemetry,
+ int action_choice, const char *client_path, int inv_choice)
+{
+ int ret;
+ char *request;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ char *command = "clients";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path = NULL;
+
+ ret = rte_telemetry_create_json_request(action_choice, command,
+ client_path, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry)
+{
+ int ret;
+ const char *client_path = TEST_CLIENT;
+
+ if (!telemetry) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ ret = rte_telemetry_create_test_socket(telemetry, client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create test request client socket");
+ return -1;
+ }
+
+ int port_ids[] = {0, 1};
+ int num_port_ids = RTE_DIM(port_ids);
+
+ static const char * const stat_names[] = {"tx_good_packets",
+ "rx_good_packets"};
+ int num_stat_names = RTE_DIM(stat_names);
+
+ static const char * const test_types[] = {
+ "INVALID ACTION VALUE TESTS",
+ "INVALID COMMAND VALUE TESTS",
+ "INVALID DATA VALUE TESTS",
+ "INVALID ACTION FIELD TESTS",
+ "INVALID COMMAND FIELD TESTS",
+ "INVALID DATA FIELD TESTS",
+ "INVALID JSON FORMAT TESTS",
+ "VALID TESTS"
+ };
+
+
+#define NUM_TEST_TYPES (sizeof(test_types)/sizeof(const char * const))
+
+ uint32_t i;
+ for (i = 0; i < NUM_TEST_TYPES; i++) {
+ TELEMETRY_LOG_INFO("%s", test_types[i]);
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "ports", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports test passed");
+
+ ret = rte_telemetry_send_get_ports_details_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports details valid");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports details invalid");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports details test passed");
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "port_stats", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get port stats valid test");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats test passed");
+
+ ret = rte_telemetry_send_stats_values_by_name_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, stat_names,
+ num_stat_names, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats values by name valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats values by name invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats values by name test passed");
+
+ ret = rte_telemetry_send_unreg_request(telemetry, ACTION_DELETE,
+ client_path, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Deregister valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Deregister invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Deregister test passed");
+ }
+
+ return 0;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.h b/lib/librte_telemetry/rte_telemetry_parser_test.h
new file mode 100644
index 0000000..6ada852
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#ifndef _RTE_TELEMETRY_PARSER_TEST_H_
+#define _RTE_TELEMETRY_PARSER_TEST_H_
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry);
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, json_t **data);
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, char **request,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice);
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_unreg_request(int action_choice, const char *client_path,
+ int inv_choice);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_socket_tests.h b/lib/librte_telemetry/rte_telemetry_socket_tests.h
new file mode 100644
index 0000000..db9167c
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_socket_tests.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdbool.h>
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_SOCKET_TESTING_H_
+#define _RTE_TELEMETRY_SOCKET_TESTING_H_
+
+int32_t
+rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry,
+ int fd);
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
+ int send_fd, int recv_fd);
+
+int32_t
+rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
+ int *recv_fd);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
index 992d227..98459fc 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -2,5 +2,6 @@ DPDK_18.11 {
global:

rte_telemetry_init;
+ rte_telemetry_selftest;
local: *;
};
--
2.9.5
Kevin Laatz
2018-10-03 17:36:10 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to enable/disable the selftest.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 43f7a93..5b87216 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -654,6 +654,7 @@ static int32_t
rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
int pid, ret;
+ int selftest = 0;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
@@ -666,18 +667,20 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

telemetry->metrics_register_done = 1;
- ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
- telemetry->server_fd);
- if (ret < 0)
- return -1;
+ if (selftest) {
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ 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;
- }
+ 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");
+ TELEMETRY_LOG_INFO("Success - All Parser Tests Passed");
+ }

return 0;
}
--
2.9.5
Kevin Laatz
2018-10-03 17:36:12 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds a python script which can be used as a demo
client. The script is interactive and will allow the user to
register, request statistics, and unregister.

To run the script, an argument for the client file path must
be passed in: "python telemetry_client.py <file_path>".

This script is useful to see how the Telemetry API for DPDK
is used, and how to make the initial connection.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
---
usertools/dpdk-telemetry-client.py | 116 +++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 usertools/dpdk-telemetry-client.py

diff --git a/usertools/dpdk-telemetry-client.py b/usertools/dpdk-telemetry-client.py
new file mode 100644
index 0000000..ede778c
--- /dev/null
+++ b/usertools/dpdk-telemetry-client.py
@@ -0,0 +1,116 @@
+# SPDK-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+import socket
+import os
+import sys
+import time
+
+BUFFER_SIZE = 200000
+
+METRICS_REQ = "{\"action\":0,\"command\":\"ports_all_stat_values\",\"data\":null}"
+API_REG = "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\":\""
+API_UNREG = "{\"action\":2,\"command\":\"clients\",\"data\":{\"client_path\":\""
+DEFAULT_FP = "/var/run/.default_client"
+
+class Socket:
+
+ def __init__(self):
+ self.send_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.client_fd = None
+
+ def __del__(self):
+ try:
+ self.send_fd.close()
+ self.recv_fd.close()
+ self.client_fd.close()
+ except:
+ print("Error - Sockets could not be closed")
+
+class Client:
+
+ def __init__(self): # Creates a client instance
+ self.socket = Socket()
+ self.file_path = None
+ self.choice = None
+ self.unregistered = 0
+
+ def __del__(self):
+ try:
+ if self.unregistered == 0:
+ self.unregister();
+ except:
+ print("Error - Client could not be destroyed")
+
+ def getFilepath(self, file_path): # Gets arguments from Command-Line and assigns to instance of client
+ self.file_path = file_path
+
+ def register(self): # Connects a client to DPDK-instance
+ if os.path.exists(self.file_path):
+ os.unlink(self.file_path)
+ try:
+ self.socket.recv_fd.bind(self.file_path)
+ except socket.error as msg:
+ print ("Error - Socket binding error: " + str(msg) + "\n")
+ self.socket.recv_fd.settimeout(2)
+ self.socket.send_fd.connect("/var/run/.rte_telemetry")
+ JSON = (API_REG + self.file_path + "\"}}")
+ self.socket.send_fd.sendall(JSON)
+ self.socket.recv_fd.listen(1)
+ self.socket.client_fd = self.socket.recv_fd.accept()[0]
+
+ def unregister(self): # Unregister a given client
+ self.socket.client_fd.send(API_UNREG + self.file_path + "\"}}")
+ self.socket.client_fd.close()
+
+ def requestMetrics(self): # Requests metrics for given client
+ self.socket.client_fd.send(METRICS_REQ)
+ data = self.socket.client_fd.recv(BUFFER_SIZE)
+ print "\nResponse: \n", str(data)
+
+ def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client
+ print("\nPlease enter the number of times you'd like to continuously request Metrics:")
+ n_requests = int(input("\n:"))
+ print("\033[F") #Removes the user input from screen, cleans it up
+ print("\033[K")
+ for i in range(n_requests):
+ self.requestMetrics()
+ time.sleep(sleep_time)
+
+ def interactiveMenu(self, sleep_time): # Creates Interactive menu within the script
+ while self.choice != 3:
+ print("\nOptions Menu")
+ print("[1] Send for Metrics for all ports")
+ print("[2] Send for Metrics for all ports recursively")
+ print("[3] Unregister client")
+
+ try:
+ self.choice = int(input("\n:"))
+ print("\033[F") #Removes the user input for screen, cleans it up
+ print("\033[K")
+ if self.choice == 1:
+ self.requestMetrics()
+ elif self.choice == 2:
+ self.repeatedlyRequestMetrics(sleep_time)
+ elif self.choice == 3:
+ self.unregister()
+ self.unregistered = 1
+ else:
+ print("Error - Invalid request choice")
+ except:
+ pass
+
+if __name__ == "__main__":
+
+ sleep_time = 1
+ file_path = ""
+ if (len(sys.argv) == 2):
+ file_path = sys.argv[1]
+ else:
+ print("Warning - No filepath passed, using default (" + DEFAULT_FP + ").")
+ file_path = DEFAULT_FP
+ client = Client()
+ client.getFilepath(file_path)
+ client.register()
+ client.interactiveMenu(sleep_time)
--
2.9.5
Kevin Laatz
2018-10-03 17:36:11 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds all documentation for telemetry.

A description on how to use the Telemetry API with a DPDK
application is given in this document.

It also adds the MAINTAINERS file entry for telemetry.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
MAINTAINERS | 5 +++
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 85 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 91 insertions(+)
create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 9fd258f..181eaf0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1156,6 +1156,11 @@ F: test/bpf/
F: test/test/test_bpf.c
F: doc/guides/prog_guide/bpf_lib.rst

+Telemetry
+M: Kevin Laatz <***@intel.com>
+F: lib/librte_telemetry/
+F: usertools/dpdk-telemetry-client.py
+F: doc/guides/howto/telemetry.rst

Test Applications
-----------------
diff --git a/doc/guides/howto/index.rst b/doc/guides/howto/index.rst
index e13a090..a642a2b 100644
--- a/doc/guides/howto/index.rst
+++ b/doc/guides/howto/index.rst
@@ -17,3 +17,4 @@ HowTo Guides
virtio_user_for_container_networking
virtio_user_as_exceptional_path
packet_capture_framework
+ telemetry
diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst
new file mode 100644
index 0000000..3fcb061
--- /dev/null
+++ b/doc/guides/howto/telemetry.rst
@@ -0,0 +1,85 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright(c) 2018 Intel Corporation.
+
+DPDK Telemetry API User Guide
+==============================
+
+This document describes how the Data Plane Development Kit(DPDK) Telemetry API
+is used for querying port statistics from incoming traffic.
+
+Introduction
+------------
+
+The ``librte_telemetry`` provides the functionality so that users may query
+metrics from incoming port traffic. The application which initializes packet
+forwarding will act as the server, sending metrics to the requesting application
+which acts as the client.
+
+In DPDK, applications are used to initialize the ``telemetry``. To view incoming
+traffic on featured ports, the application should be run first (ie. after ports
+are configured). Once the application is running, the service assurance agent
+(for example the collectd plugin) should be run to begin querying the API.
+
+A client connects their Service Assurance application to the DPDK application
+via a UNIX socket. Once a connection is established, a client can send JSON
+messages to the DPDK application requesting metrics via another UNIX client.
+This request is then handled and parsed if valid. The response is then
+formatted in JSON and sent back to the requesting client.
+
+Pre-requisites
+~~~~~~~~~~~~~~
+
+* Python ≥ 2.5
+
+* Jansson library for JSON serialization
+
+Test Environment
+----------------
+
+``telemetry`` offers a range of selftests that a client can run within
+the DPDK application.
+
+Selftests are disabled by default. They can be enabled by setting the 'selftest'
+variable to 1 in rte_telemetry_initial_accept().
+
+Note: this 'hardcoded' value is temporary.
+
+Configuration
+-------------
+
+Enable the telemetry API by modifying the following config option before
+building DPDK::
+
+ CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+Note: Meson will pick this up automatically if ``libjansson`` is available.
+
+Running the Application
+-----------------------
+
+The following steps demonstrate how to run the ``telemetry`` API to query all
+statistics on all active ports, using the ``telemetry_client`` python script
+to query.
+Note: This guide assumes packet generation is applicable and the user is
+testing with testpmd as a DPDK primary application to forward packets, although
+any DPDK application is applicable.
+
+#. Launch testpmd as the primary application with ``telemetry``.::
+
+ ./app/testpmd --telemetry
+
+#. Launch the ``telemetry`` python script with a client filepath.::
+
+ python usertools/telemetry_client.py /var/run/some_client
+
+ The client filepath is going to be used to setup our UNIX connection with the
+ DPDK primary application, in this case ``testpmd``
+ This will initialize a menu where a client can proceed to recursively query
+ statistics, request statistics once or unregister the file_path, thus exiting
+ the menu.
+
+#. Send traffic to any or all available ports from a traffic generator.
+ Select a query option(recursive or singular polling).
+ The metrics will then be displayed on the client terminal in JSON format.
+
+#. Once finished, unregister the client using the menu command.
--
2.9.5
Van Haaren, Harry
2018-10-04 13:00:14 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Wednesday, October 3, 2018 6:36 PM
Subject: [PATCH v2 00/10] introduce telemetry library
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being set
to 0, --telemetry said to be 'unrecognized' as a startup print. This is a
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!


goto TL_DR; // if time is short :)


In this v2 patchset, we've reworked the Telemetry to no longer use the vdev
infrastructure, instead having EAL enable it directly. This was requested as
feedback to the v1 patchset. I'll detail the approach below, and highlight
some issues we identified while implementing it.

Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs etc for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL initialize
Telemetry, it must depend on it - this causes a circular dependency.

This patchset resolves that circular dependency by using a "weak" symbol for
telemetry init, and then the "strong" version of telemetry init will replace
it when the library is compiled in. Although this *technically* works, it requires
that applications *LINK* against Telemetry library explicitly - as EAL won't pull
in the Telemetry .so automatically... This means application-level build-system
changes to enable --telemetry on the DPDK EAL command line.

Given the complexity in enabling EAL to handle the Telemetry init() and its
dependencies, I'd like to ask you folks for input on how to better solve this?


TL_DR;

I'll re-suggest the --vdev as an option. It might not be perfect,
but in my opinion it's a better solution than the v2 solution here...

Input and suggestions welcome, as you know integration is coming
close so sooner is better!


Regards, -Harry
Van Haaren, Harry
2018-10-04 13:25:51 UTC
Permalink
-----Original Message-----
From: Van Haaren, Harry
Sent: Thursday, October 4, 2018 2:00 PM
Subject: RE: [PATCH v2 00/10] introduce telemetry library
-----Original Message-----
From: Laatz, Kevin
Sent: Wednesday, October 3, 2018 6:36 PM
Subject: [PATCH v2 00/10] introduce telemetry library
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being set
to 0, --telemetry said to be 'unrecognized' as a startup print. This is a
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!
goto TL_DR; // if time is short :)
In this v2 patchset, we've reworked the Telemetry to no longer use the vdev
infrastructure, instead having EAL enable it directly. This was requested as
feedback to the v1 patchset. I'll detail the approach below, and highlight
some issues we identified while implementing it.
Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs etc
for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL
initialize
Telemetry, it must depend on it - this causes a circular dependency.
This patchset resolves that circular dependency by using a "weak" symbol for
telemetry init, and then the "strong" version of telemetry init will replace
it when the library is compiled in.
Correction: we attempted this approach - but ended up adding a TAILQ of library
initializers functions to EAL, which was then iterated during rte_eal_init().
This also resolved the circular-dependency, but the same linking issue as
described below still exists.

So - the same question still stands - what is the best solution for 18.11?
Although this *technically* works, it
requires
that applications *LINK* against Telemetry library explicitly - as EAL won't
pull
in the Telemetry .so automatically... This means application-level build-
system
changes to enable --telemetry on the DPDK EAL command line.
Given the complexity in enabling EAL to handle the Telemetry init() and its
dependencies, I'd like to ask you folks for input on how to better solve
this?
TL_DR;
I'll re-suggest the --vdev as an option. It might not be perfect,
but in my opinion it's a better solution than the v2 solution here...
Input and suggestions welcome, as you know integration is coming
close so sooner is better!
Regards, -Harry
Gaëtan Rivet
2018-10-04 15:16:49 UTC
Permalink
Hi Harry,
Post by Van Haaren, Harry
-----Original Message-----
From: Van Haaren, Harry
Sent: Thursday, October 4, 2018 2:00 PM
Subject: RE: [PATCH v2 00/10] introduce telemetry library
-----Original Message-----
From: Laatz, Kevin
Sent: Wednesday, October 3, 2018 6:36 PM
Subject: [PATCH v2 00/10] introduce telemetry library
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being set
to 0, --telemetry said to be 'unrecognized' as a startup print. This is a
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!
goto TL_DR; // if time is short :)
In this v2 patchset, we've reworked the Telemetry to no longer use the vdev
infrastructure, instead having EAL enable it directly. This was requested as
feedback to the v1 patchset. I'll detail the approach below, and highlight
some issues we identified while implementing it.
Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs etc
for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL
initialize
Telemetry, it must depend on it - this causes a circular dependency.
This patchset resolves that circular dependency by using a "weak" symbol for
telemetry init, and then the "strong" version of telemetry init will replace
it when the library is compiled in.
Correction: we attempted this approach - but ended up adding a TAILQ of library
initializers functions to EAL, which was then iterated during rte_eal_init().
This also resolved the circular-dependency, but the same linking issue as
described below still exists.
So - the same question still stands - what is the best solution for 18.11?
Although this *technically* works, it
requires
that applications *LINK* against Telemetry library explicitly - as EAL won't
pull
in the Telemetry .so automatically... This means application-level build-
system
changes to enable --telemetry on the DPDK EAL command line.
Given the complexity in enabling EAL to handle the Telemetry init() and its
dependencies, I'd like to ask you folks for input on how to better solve
this?
I think the v2 is better. I have suggested a few changes, but I think
you almost have a final version.

Is it not possible to use -d or to put the .so in the solib_dir?
If you have symbols to solve at link-time, then you have already
modified you app anyway (doesn't concern rte_telemetry, but for general
lib consideration). If not, you can ask EAL to load it dynamically, so
it should be ok?

Sorry, none of our clients are using DPDK in shared mode, I'm not
familiar with the process here. But I'm convinced that libs
should not subvert PMD facilities to get their way, and if something is
missing it could be useful to other libraries as well, so it might as
well be cleanly handled.
--
Gaëtan Rivet
6WIND
Thomas Monjalon
2018-10-04 15:53:53 UTC
Permalink
From: Van Haaren, Harry
From: Laatz, Kevin
Post by Ciara Power
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Post by Ciara Power
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being set
to 0, --telemetry said to be 'unrecognized' as a startup print. This is a
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!
goto TL_DR; // if time is short :)
In this v2 patchset, we've reworked the Telemetry to no longer use the vdev
infrastructure, instead having EAL enable it directly. This was requested as
feedback to the v1 patchset. I'll detail the approach below, and highlight
some issues we identified while implementing it.
Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs etc
for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL
initialize
Telemetry, it must depend on it - this causes a circular dependency.
This patchset resolves that circular dependency by using a "weak" symbol for
telemetry init, and then the "strong" version of telemetry init will replace
it when the library is compiled in.
Correction: we attempted this approach - but ended up adding a TAILQ of library
initializers functions to EAL, which was then iterated during rte_eal_init().
This also resolved the circular-dependency, but the same linking issue as
described below still exists.
So - the same question still stands - what is the best solution for 18.11?
Although this *technically* works, it
requires
that applications *LINK* against Telemetry library explicitly - as EAL won't
pull
in the Telemetry .so automatically... This means application-level build-
system
changes to enable --telemetry on the DPDK EAL command line.
Given the complexity in enabling EAL to handle the Telemetry init() and its
dependencies, I'd like to ask you folks for input on how to better solve
this?
First, the telemetry feature must be enabled via a public function (API).
The application can decide to enable the feature at any time, right?
If the application wants to enable the feature at initialization
(and considers user input from the command line),
then the init function has a dependency on telemetry.
Your dependency concern is that the init function (which is high level)
is in EAL (which is the lowest layer in DPDK).

I think the command line should not be managed directly by EAL.
My suggestion in last summit was to move this code in a different library.
We should also move the init function(s) to a new high level library.

This is my proposal to solve cyclic dependency: move rte_eal_init in a lib
which depends on everything.

About the linking issue, I don't understand the problem.
If you use the DPDK makefiles, rte.app.mk should manage it.
If you use the DPDK meson, all libs are linked.
If you use your own system, of course you need to add telemetry lib.
Gaëtan Rivet
2018-10-05 22:05:09 UTC
Permalink
Post by Thomas Monjalon
From: Van Haaren, Harry
From: Laatz, Kevin
Post by Ciara Power
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Post by Ciara Power
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being set
to 0, --telemetry said to be 'unrecognized' as a startup print. This is a
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!
goto TL_DR; // if time is short :)
In this v2 patchset, we've reworked the Telemetry to no longer use the vdev
infrastructure, instead having EAL enable it directly. This was requested as
feedback to the v1 patchset. I'll detail the approach below, and highlight
some issues we identified while implementing it.
Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs etc
for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL
initialize
Telemetry, it must depend on it - this causes a circular dependency.
This patchset resolves that circular dependency by using a "weak" symbol for
telemetry init, and then the "strong" version of telemetry init will replace
it when the library is compiled in.
Correction: we attempted this approach - but ended up adding a TAILQ of library
initializers functions to EAL, which was then iterated during rte_eal_init().
This also resolved the circular-dependency, but the same linking issue as
described below still exists.
So - the same question still stands - what is the best solution for 18.11?
Although this *technically* works, it
requires
that applications *LINK* against Telemetry library explicitly - as EAL won't
pull
in the Telemetry .so automatically... This means application-level build-
system
changes to enable --telemetry on the DPDK EAL command line.
Given the complexity in enabling EAL to handle the Telemetry init() and its
dependencies, I'd like to ask you folks for input on how to better solve
this?
First, the telemetry feature must be enabled via a public function (API).
The application can decide to enable the feature at any time, right?
If the application wants to enable the feature at initialization
(and considers user input from the command line),
then the init function has a dependency on telemetry.
Your dependency concern is that the init function (which is high level)
is in EAL (which is the lowest layer in DPDK).
I think the command line should not be managed directly by EAL.
My suggestion in last summit was to move this code in a different library.
We should also move the init function(s) to a new high level library.
Part of the proposed solution here is to add a thin layer in EAL to
register new parameters. I think this could kickstart what you want to
do.
--
Gaëtan Rivet
6WIND
Van Haaren, Harry
2018-10-09 10:33:58 UTC
Permalink
-----Original Message-----
Sent: Thursday, October 4, 2018 4:54 PM
Subject: Re: [PATCH v2 00/10] introduce telemetry library
From: Van Haaren, Harry
From: Laatz, Kevin
Post by Ciara Power
This patchset introduces a Telemetry library for DPDK Service
Assurance.
Post by Ciara Power
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Post by Ciara Power
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being
set
Post by Ciara Power
to 0, --telemetry said to be 'unrecognized' as a startup print. This
is a
Post by Ciara Power
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!
goto TL_DR; // if time is short :)
In this v2 patchset, we've reworked the Telemetry to no longer use the
vdev
infrastructure, instead having EAL enable it directly. This was
requested as
feedback to the v1 patchset. I'll detail the approach below, and
highlight
some issues we identified while implementing it.
Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs
etc
for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL
initialize
Telemetry, it must depend on it - this causes a circular dependency.
This patchset resolves that circular dependency by using a "weak" symbol
for
telemetry init, and then the "strong" version of telemetry init will
replace
it when the library is compiled in.
Correction: we attempted this approach - but ended up adding a TAILQ of
library
initializers functions to EAL, which was then iterated during
rte_eal_init().
This also resolved the circular-dependency, but the same linking issue as
described below still exists.
So - the same question still stands - what is the best solution for 18.11?
Although this *technically* works, it
requires
that applications *LINK* against Telemetry library explicitly - as EAL
won't
pull
in the Telemetry .so automatically... This means application-level
build-
system
changes to enable --telemetry on the DPDK EAL command line.
Given the complexity in enabling EAL to handle the Telemetry init() and
its
dependencies, I'd like to ask you folks for input on how to better solve
this?
First, the telemetry feature must be enabled via a public function (API).
The application can decide to enable the feature at any time, right?
If the application wants to enable the feature at initialization
(and considers user input from the command line),
then the init function has a dependency on telemetry.
Your dependency concern is that the init function (which is high level)
is in EAL (which is the lowest layer in DPDK).
Yes, and this has been resolved by allowing components to register
with EAL to have their _init() function called later. V3 coming up
with this approach, it seems to cover the required use-cases.
I think the command line should not be managed directly by EAL.
My suggestion in last summit was to move this code in a different library.
We should also move the init function(s) to a new high level library.
This is my proposal to solve cyclic dependency: move rte_eal_init in a lib
which depends on everything.
I have prototyped this approach, and it is not really clean. It means
splitting EAL into two halves, and due to meson library naming we have
to move all eal files to eal_impl or something, and then eal.so keeps rte_eal_init().

Removing functions from the .map files is also technically an ABI break,
at which point I didn't think it was the right solution.
About the linking issue, I don't understand the problem.
If you use the DPDK makefiles, rte.app.mk should manage it.
If you use the DPDK meson, all libs are linked.
If you use your own system, of course you need to add telemetry lib.
Yes agreed, in practice it should be exactly like this. In reality
it can be harder to achieve the exact dependencies correctly with
both Static/Shared builds and constructors etc.

I believe the current approach of registering an _init() function
will be acceptable, let's wait for v3 to hit the mailing list.
Thomas Monjalon
2018-10-09 11:41:10 UTC
Permalink
Post by Van Haaren, Harry
Post by Ciara Power
From: Van Haaren, Harry
From: Laatz, Kevin
Post by Ciara Power
This patchset introduces a Telemetry library for DPDK Service
Assurance.
Post by Ciara Power
This library provides an easy way to query DPDK Ethdev metrics.
<snip>
Post by Ciara Power
Note: We are aware that the --telemetry flag is not working for meson
builds, we are working on it for a future patch. Despite opterr being
set
Post by Ciara Power
to 0, --telemetry said to be 'unrecognized' as a startup print. This
is a
Post by Ciara Power
cosmetic issue and will also be addressed.
---
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback
Hi Gaetan, Thomas, Stephen and Shreyansh!
goto TL_DR; // if time is short :)
In this v2 patchset, we've reworked the Telemetry to no longer use the
vdev
infrastructure, instead having EAL enable it directly. This was
requested as
feedback to the v1 patchset. I'll detail the approach below, and
highlight
some issues we identified while implementing it.
Currently, EAL does not depend on any "DPDK" libraries (ignore kvargs
etc
for a minute).
Telemetry is a DPDK library, so it depends on EAL. In order to have EAL
initialize
Telemetry, it must depend on it - this causes a circular dependency.
This patchset resolves that circular dependency by using a "weak" symbol
for
telemetry init, and then the "strong" version of telemetry init will
replace
it when the library is compiled in.
Correction: we attempted this approach - but ended up adding a TAILQ of
library
initializers functions to EAL, which was then iterated during
rte_eal_init().
This also resolved the circular-dependency, but the same linking issue as
described below still exists.
So - the same question still stands - what is the best solution for 18.11?
Although this *technically* works, it
requires
that applications *LINK* against Telemetry library explicitly - as EAL
won't
pull
in the Telemetry .so automatically... This means application-level
build-
system
changes to enable --telemetry on the DPDK EAL command line.
Given the complexity in enabling EAL to handle the Telemetry init() and
its
dependencies, I'd like to ask you folks for input on how to better solve
this?
First, the telemetry feature must be enabled via a public function (API).
The application can decide to enable the feature at any time, right?
If the application wants to enable the feature at initialization
(and considers user input from the command line),
then the init function has a dependency on telemetry.
Your dependency concern is that the init function (which is high level)
is in EAL (which is the lowest layer in DPDK).
Yes, and this has been resolved by allowing components to register
with EAL to have their _init() function called later. V3 coming up
with this approach, it seems to cover the required use-cases.
Post by Ciara Power
I think the command line should not be managed directly by EAL.
My suggestion in last summit was to move this code in a different library.
We should also move the init function(s) to a new high level library.
This is my proposal to solve cyclic dependency: move rte_eal_init in a lib
which depends on everything.
I have prototyped this approach, and it is not really clean. It means
splitting EAL into two halves, and due to meson library naming we have
to move all eal files to eal_impl or something, and then eal.so keeps rte_eal_init().
Removing functions from the .map files is also technically an ABI break,
at which point I didn't think it was the right solution.
Post by Ciara Power
About the linking issue, I don't understand the problem.
If you use the DPDK makefiles, rte.app.mk should manage it.
If you use the DPDK meson, all libs are linked.
If you use your own system, of course you need to add telemetry lib.
Yes agreed, in practice it should be exactly like this. In reality
it can be harder to achieve the exact dependencies correctly with
both Static/Shared builds and constructors etc.
I believe the current approach of registering an _init() function
will be acceptable, let's wait for v3 to hit the mailing list.
I think it is not clean.
We should really split EAL in two parts:
- low level routines
- high level init.

About telemetry, you can find any workaround, but it must be temporary.
Bruce Richardson
2018-10-09 14:56:01 UTC
Permalink
Post by Thomas Monjalon
I think it is not clean.
- low level routines
- high level init.
About telemetry, you can find any workaround, but it must be temporary.
In fairness, though, splitting up EAL is a fairly significant piece of work
to just throw out there as a suggestion to people! Have you investigated
what it would take for that, or looked at the implications of it? It's
probably not something that one can just sit and do in the spur of the
moment.

Regards,
/Bruce
Thomas Monjalon
2018-10-09 17:07:11 UTC
Permalink
Post by Bruce Richardson
Post by Thomas Monjalon
I think it is not clean.
- low level routines
- high level init.
About telemetry, you can find any workaround, but it must be temporary.
In fairness, though, splitting up EAL is a fairly significant piece of work
to just throw out there as a suggestion to people! Have you investigated
what it would take for that, or looked at the implications of it? It's
probably not something that one can just sit and do in the spur of the
moment.
For sure, it is a massive work.
That's why I agree to have a workaround in the meantime.

I did not check the implications in details.
The issue with such clean-up of DPDK design is to find someone
willing to work on it.
Kevin Laatz
2018-10-10 10:51:22 UTC
Permalink
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.

The telemetry library provides a method for a service assurance component
to retrieve metrics from a DPDK packet forwarding application.
Communicating from the service assurance component to DPDK is done using a
UNIX domain socket, passing a JSON formatted string. A reply is sent (again
a JSON formatted string) of the current DPDK metrics.

The telemetry component makes use of the existing rte_metrics library to
query values. The values to be transmitted via the telemetry infrastructure
must be present in the Metrics library. Currently the ethdev values are
pushed to the metrics library, and the queried from there there is an open
question on how applications would like this to occur. Currently only
ethdev to metrics functionality is implemented, however other subsystems
like crypto, eventdev, keepalive etc can use similar mechanisms.

Exposing DPDK Telemetry via a socket interface enables service assurance
agents like collectd to consume data from DPDK. This is vital for
monitoring, fault-detection, and error reporting. A collectd plugin has
been created to interact with the DPDK Telemetry component, showing how it
can be used in practice. The collectd plugin will be upstreamed to collectd
at a later stage. A small python script is provided in
./usertools/telemetry_client.py to quick-start using DPDK Telemetry.

Note: Despite opterr being set to 0, --telemetry said to be 'unrecognized'
as a startup print. This is a cosmetic issue and will be addressed in the
future.

---
v2:
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback

v3:
- Reworked registering with eal and moved to rte_param (Gaetan)
- Added BSD implementation for rte_param (Gaetan)
- Updated the paths to align with the new runtime file location (Mattias)
- Fixed pointer checks to align with the coding style 1.8.1 (Mattias)
- Added missing decref's and close's (Mattias)
- Fixed runtime issue in Meson (was not recognising flag due to linking)
- More general clean up


Ciara Power, Brian Archbold and Kevin Laatz (10):
telemetry: initial telemetry infrastructure
telemetry: add initial connection socket
telemetry: add client feature and sockets
telemetry: add parser for client socket messages
telemetry: update metrics before sending stats
telemetry: format json response when sending stats
telemetry: add tests for telemetry api
telemetry: add ability to disable selftest
doc: add telemetry documentation
usertools: add client python script for telemetry

Kevin Laatz (2):
eal: add param register infrastructure
build: add dependency on telemetry to apps in meson

MAINTAINERS | 5 +
app/meson.build | 4 +-
app/pdump/meson.build | 2 +-
app/proc-info/meson.build | 2 +-
app/test-bbdev/meson.build | 2 +-
app/test-crypto-perf/meson.build | 2 +-
app/test-pmd/meson.build | 2 +-
config/common_base | 5 +
config/meson.build | 3 +
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 85 +
lib/Makefile | 2 +
lib/librte_eal/bsdapp/eal/Makefile | 1 +
lib/librte_eal/bsdapp/eal/eal.c | 18 +-
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_param.h | 64 +
lib/librte_eal/common/meson.build | 2 +
lib/librte_eal/common/rte_param.c | 44 +
lib/librte_eal/linuxapp/eal/Makefile | 1 +
lib/librte_eal/linuxapp/eal/eal.c | 18 +-
lib/librte_eal/rte_eal_version.map | 1 +
lib/librte_telemetry/Makefile | 30 +
lib/librte_telemetry/meson.build | 9 +
lib/librte_telemetry/rte_telemetry.c | 1803 +++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 48 +
lib/librte_telemetry/rte_telemetry_internal.h | 81 +
lib/librte_telemetry/rte_telemetry_parser.c | 586 +++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 534 ++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 +
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 +
lib/librte_telemetry/rte_telemetry_version.map | 7 +
lib/meson.build | 3 +-
meson.build | 1 +
mk/rte.app.mk | 1 +
usertools/dpdk-telemetry-client.py | 116 ++
36 files changed, 3562 insertions(+), 10 deletions(-)
create mode 100644 doc/guides/howto/telemetry.rst
create mode 100644 lib/librte_eal/common/include/rte_param.h
create mode 100644 lib/librte_eal/common/rte_param.c
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map
create mode 100644 usertools/dpdk-telemetry-client.py
--
2.9.5
Kevin Laatz
2018-10-10 10:51:23 UTC
Permalink
This commit adds infrastructure to EAL that allows an application to
register it's init function with EAL. This allows libraries to be
initialized at the end of EAL init.

This infrastructure allows libraries that depend on EAL to be initialized
as part of EAL init, removing circular dependency issues.

Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_eal/bsdapp/eal/Makefile | 1 +
lib/librte_eal/bsdapp/eal/eal.c | 18 ++++++++-
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_param.h | 64 +++++++++++++++++++++++++++++++
lib/librte_eal/common/meson.build | 2 +
lib/librte_eal/common/rte_param.c | 44 +++++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 1 +
lib/librte_eal/linuxapp/eal/eal.c | 18 ++++++++-
lib/librte_eal/rte_eal_version.map | 1 +
9 files changed, 148 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_param.h
create mode 100644 lib/librte_eal/common/rte_param.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d27da3d..7f4fa7e 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -66,6 +66,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_elem.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_heap.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_mp.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_keepalive.c
+SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_param.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_service.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_reciprocal.c

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index d7ae9d6..27b7afc 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -42,6 +42,7 @@
#include <rte_devargs.h>
#include <rte_version.h>
#include <rte_vfio.h>
+#include <rte_param.h>
#include <rte_atomic.h>
#include <malloc_heap.h>

@@ -414,12 +415,20 @@ eal_parse_args(int argc, char **argv)
argvopt = argv;
optind = 1;
optreset = 1;
+ opterr = 0;

while ((opt = getopt_long(argc, argvopt, eal_short_options,
eal_long_options, &option_index)) != EOF) {

- /* getopt is not happy, stop right now */
+ /*
+ * getopt didn't recognise the option, lets parse the
+ * registered options to see if the flag is valid
+ */
if (opt == '?') {
+ ret = rte_param_parse(argv[optind-1]);
+ if (ret == 0)
+ continue;
+
eal_usage(prgname);
ret = -1;
goto out;
@@ -788,6 +797,13 @@ rte_eal_init(int argc, char **argv)

rte_eal_mcfg_complete();

+ /* Call each registered callback, if enabled */
+ ret = rte_param_init();
+ if (ret < 0) {
+ rte_eal_init_alert("Callback failed\n");
+ return -1;
+ }
+
return fctret;
}

diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca6882..8def95a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -12,6 +12,7 @@ INC += rte_tailq.h rte_interrupts.h rte_alarm.h
INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_bus.h rte_dev.h rte_class.h
+INC += rte_param.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
INC += rte_malloc.h rte_keepalive.h rte_time.h
INC += rte_service.h rte_service_component.h
diff --git a/lib/librte_eal/common/include/rte_param.h b/lib/librte_eal/common/include/rte_param.h
new file mode 100644
index 0000000..316d03a
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_param.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_PARAM_H__
+#define __INCLUDE_RTE_PARAM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_param_cb)(void);
+
+struct rte_param {
+ TAILQ_ENTRY(rte_param) next;
+ char *eal_flag;
+ char *help_text;
+ rte_param_cb cb;
+ int enabled;
+};
+
+/**
+ * @internal Check if the passed flag is valid
+ *
+ * @param flag
+ * The flag to be parsed
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -1 on fail
+ */
+int
+rte_param_parse(char *flag);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register a function with EAL. Registering the function will enable the
+ * function to be called at the end of EAL init.
+ *
+ * @param reg_param
+ * rte_param structure
+ */
+void __rte_experimental
+rte_param_register(struct rte_param *reg_param);
+
+/**
+ * @internal Iterate through the registered params and init the enabled ones
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -1 on fail
+ */
+int
+rte_param_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index b7fc984..4069e49 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -33,6 +33,7 @@ common_sources = files(
'malloc_mp.c',
'rte_keepalive.c',
'rte_malloc.c',
+ 'rte_param.c',
'rte_reciprocal.c',
'rte_service.c'
)
@@ -70,6 +71,7 @@ common_headers = files(
'include/rte_malloc_heap.h',
'include/rte_memory.h',
'include/rte_memzone.h',
+ 'include/rte_param.h',
'include/rte_pci_dev_feature_defs.h',
'include/rte_pci_dev_features.h',
'include/rte_per_lcore.h',
diff --git a/lib/librte_eal/common/rte_param.c b/lib/librte_eal/common/rte_param.c
new file mode 100644
index 0000000..2ef3ba9
--- /dev/null
+++ b/lib/librte_eal/common/rte_param.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_param.h>
+
+TAILQ_HEAD(rte_param_list, rte_param);
+
+struct rte_param_list rte_param_list =
+ TAILQ_HEAD_INITIALIZER(rte_param_list);
+
+static struct rte_param *param;
+
+int
+rte_param_parse(char *flag) {
+ /* Check if the flag is in the registered inits */
+ TAILQ_FOREACH(param, &rte_param_list, next) {
+ if (strcmp(flag, param->eal_flag) == 0) {
+ param->enabled = 1;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void __rte_experimental
+rte_param_register(struct rte_param *reg_param) {
+ TAILQ_INSERT_HEAD(&rte_param_list, reg_param, next);
+}
+
+int
+rte_param_init(void) {
+ TAILQ_FOREACH(param, &rte_param_list, next) {
+ if (param->enabled)
+ param->cb();
+ }
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 5c16bc4..2bf8b24 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -74,6 +74,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_elem.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_heap.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_mp.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_keepalive.c
+SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_param.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_service.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_reciprocal.c

diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 4a55d3b..e28562b 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -48,6 +48,7 @@
#include <rte_atomic.h>
#include <malloc_heap.h>
#include <rte_vfio.h>
+#include <rte_param.h>

#include "eal_private.h"
#include "eal_thread.h"
@@ -600,12 +601,20 @@ eal_parse_args(int argc, char **argv)

argvopt = argv;
optind = 1;
+ opterr = 0;

while ((opt = getopt_long(argc, argvopt, eal_short_options,
eal_long_options, &option_index)) != EOF) {

- /* getopt is not happy, stop right now */
+ /*
+ * getopt didn't recognise the option, lets parse the
+ * registered options to see if the flag is valid
+ */
if (opt == '?') {
+ ret = rte_param_parse(argv[optind-1]);
+ if (ret == 0)
+ continue;
+
eal_usage(prgname);
ret = -1;
goto out;
@@ -1071,6 +1080,13 @@ rte_eal_init(int argc, char **argv)

rte_eal_mcfg_complete();

+ /* Call each registered callback, if enabled */
+ ret = rte_param_init();
+ if (ret < 0) {
+ rte_eal_init_alert("Callback failed\n");
+ return -1;
+ }
+
return fctret;
}

diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 73282bb..ccfb8a2 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -341,6 +341,7 @@ EXPERIMENTAL {
rte_mp_request_sync;
rte_mp_request_async;
rte_mp_sendmsg;
+ rte_param_register;
rte_service_lcore_attr_get;
rte_service_lcore_attr_reset_all;
rte_service_may_be_active;
--
2.9.5
Thomas Monjalon
2018-10-10 12:28:08 UTC
Permalink
Post by Ciara Power
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_param.h
Please add a global doxygen comment for the file.
The context or goal is missing here.
We need to understand what is the purpose of this new API file.

Thanks
Kevin Laatz
2018-10-10 10:51:25 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.

On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 220 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
2 files changed, 224 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index a57a118..7ff488a 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,159 @@
*/

#include <unistd.h>
+#include <fcntl.h>
#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
#include <rte_param.h>
+#include <rte_string_fns.h>

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

#define SLEEP_TIME 10

+#define DEFAULT_DPDK_PATH "/var/run/dpdk/rte/telemetry"
+
+const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;

+int32_t
+rte_telemetry_is_port_active(int port_id)
+{
+ int ret;
+
+ ret = rte_eth_find_next(port_id);
+ if (ret == port_id)
+ return 1;
+
+ TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
+ port_id);
+ return 0;
+}
+
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
+{
+ int ret, num_xstats, ret_val, i;
+ struct rte_eth_xstat *eth_xstats = NULL;
+ struct rte_eth_xstat_name *eth_xstats_names = NULL;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
+ port_id, num_xstats);
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ return -ENOMEM;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ const char *xstats_names[num_xstats];
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ if (eth_xstats_names == NULL) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats_names");
+ ret_val = -ENOMEM;
+ goto free_xstats;
+ }
+
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ ret_val = rte_metrics_reg_names(xstats_names, num_xstats);
+ if (ret_val < 0) {
+ TELEMETRY_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
+ ret_val = -1;
+ goto free_xstats;
+ }
+
+ goto free_xstats;
+
+free_xstats:
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return ret_val;
+}
+
+static int32_t
+rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
+{
+ uint16_t pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ telemetry->reg_index =
+ rte_telemetry_reg_ethdev_to_metrics(pid);
+ break;
+ }
+
+ if (telemetry->reg_index < 0) {
+ TELEMETRY_LOG_ERR("Failed to register ethdev metrics");
+ return -1;
+ }
+
+ telemetry->metrics_register_done = 1;
+
+ return 0;
+}
+
+static int32_t
+rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
+{
+ int ret;
+
+ if (telemetry->accept_fd == 0 || telemetry->accept_fd == -1) {
+ 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;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int32_t
rte_telemetry_run(void *userdata)
{
+ int ret;
struct telemetry_impl *telemetry = userdata;

if (telemetry == NULL) {
@@ -27,6 +163,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_accept_new_client(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Accept and read new client failed");
+ return -1;
+ }
+
return 0;
}

@@ -50,6 +192,66 @@ static void
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 = {0};
+
+ if (telemetry == NULL)
+ return -1;
+
+ telemetry->server_fd = socket(AF_UNIX, SOCK_STREAM, 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;
+ 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");
+ free(telemetry);
+ return -EPERM;
+ }
+
+ return -1;
+}
+
int32_t
rte_telemetry_init()
{
@@ -70,6 +272,14 @@ rte_telemetry_init()

static_telemetry->socket_id = rte_socket_id();
rte_metrics_init(static_telemetry->socket_id);
+ 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;
+ }
+
pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -89,11 +299,21 @@ rte_telemetry_init()
int32_t
rte_telemetry_cleanup(void)
{
+ int ret;
struct telemetry_impl *telemetry = static_telemetry;
+
+ 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;
}

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 4e810a8..569d56a 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -24,9 +24,13 @@ extern int telemetry_log_level;
TELEMETRY_LOG(INFO, fmt, ## args)

typedef struct telemetry_impl {
+ int accept_fd;
+ int server_fd;
pthread_t thread_id;
int thread_status;
uint32_t socket_id;
+ int reg_index;
+ int metrics_register_done;
} telemetry_impl;

#endif
--
2.9.5
Thomas Monjalon
2018-10-10 12:24:28 UTC
Permalink
Post by Ciara Power
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
+#define DEFAULT_DPDK_PATH "/var/run/dpdk/rte/telemetry"
As suggested in v2, please use eal_get_runtime_dir()
Kevin Laatz
2018-10-10 10:51:26 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch introduces clients to the telemetry API.

When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.

A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.

Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/meson.build | 2 +
lib/librte_telemetry/rte_telemetry.c | 370 +++++++++++++++++++++++++-
lib/librte_telemetry/rte_telemetry_internal.h | 25 ++
mk/rte.app.mk | 2 +-
4 files changed, 395 insertions(+), 4 deletions(-)

diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076..0ccfa36 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_telemetry.c')
headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
+jansson = cc.find_library('jansson', required: true)
+ext_deps += jansson
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 7ff488a..22bb554 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -7,6 +7,7 @@
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <jansson.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
@@ -17,6 +18,8 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

+#define BUF_SIZE 1024
+#define ACTION_POST 1
#define SLEEP_TIME 10

#define DEFAULT_DPDK_PATH "/var/run/dpdk/rte/telemetry"
@@ -35,6 +38,91 @@ rte_telemetry_is_port_active(int port_id)

TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
port_id);
+
+ return 0;
+}
+
+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, JSON_INDENT(2));
+ 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;
}

@@ -111,8 +199,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
uint16_t pid;

RTE_ETH_FOREACH_DEV(pid) {
- telemetry->reg_index =
- rte_telemetry_reg_ethdev_to_metrics(pid);
+ telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
break;
}

@@ -127,6 +214,40 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+ char buf[BUF_SIZE];
+ int ret, buffer_read = 0;
+
+ buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+ buf[buffer_read] = '\0';
+
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Read error");
+ return -1;
+ } else if (buffer_read == 0) {
+ goto close_socket;
+ } else {
+ ret = rte_telemetry_parse_client_message(telemetry, buf);
+ if (ret < 0)
+ TELEMETRY_LOG_WARN("Parse message failed");
+ goto close_socket;
+ }
+
+ return 0;
+
+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;
@@ -137,8 +258,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
TELEMETRY_LOG_ERR("Listening error with server fd");
return -1;
}
- telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);

+ 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);
@@ -147,6 +268,30 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
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)
+{
+ 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);
+ client_buf[bytes] = '\0';
+
+ if (bytes > 0)
+ telemetry->request_client = client;
}

return 0;
@@ -169,6 +314,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_read_client_sockets(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Client socket read failed");
+ return -1;
+ }
+
return 0;
}

@@ -279,6 +430,7 @@ rte_telemetry_init()
TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
return -EPERM;
}
+ TAILQ_INIT(&static_telemetry->client_list_head);

pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
@@ -296,11 +448,39 @@ rte_telemetry_init()
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) {
@@ -317,6 +497,190 @@ rte_telemetry_cleanup(void)
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 = {0};
+
+ 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_STREAM, 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");
+ json_decref(root);
+ 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");
+ return -1;
+}
+
int telemetry_log_level;
RTE_INIT(rte_telemetry_register);

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 569d56a..e3292cf 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -3,6 +3,7 @@
*/

#include <rte_log.h>
+#include <rte_tailq.h>

#ifndef _RTE_TELEMETRY_INTERNAL_H_
#define _RTE_TELEMETRY_INTERNAL_H_
@@ -23,6 +24,12 @@ extern int telemetry_log_level;
#define TELEMETRY_LOG_INFO(fmt, args...) \
TELEMETRY_LOG(INFO, fmt, ## args)

+typedef struct telemetry_client {
+ char *file_path;
+ int fd;
+ TAILQ_ENTRY(telemetry_client) client_list;
+} telemetry_client;
+
typedef struct telemetry_impl {
int accept_fd;
int server_fd;
@@ -31,6 +38,24 @@ typedef struct telemetry_impl {
uint32_t socket_id;
int reg_index;
int metrics_register_done;
+ TAILQ_HEAD(, telemetry_client) client_list_head;
+ struct telemetry_client *request_client;
+ int register_fail_count;
} telemetry_impl;

+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);
+
+int32_t
+rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
+ int error_type);
+
+int32_t
+rte_telemetry_register_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
+int32_t
+rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
#endif
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 35594b9..b740691 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
-_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry -ljansson
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
Kevin Laatz
2018-10-10 10:51:24 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the infrastructure and initial code for the telemetry
library.

The telemetry init is registered with eal_init(). We can then check to see
if --telemetry was passed as an eal flag. If --telemetry was parsed, then
we call telemetry init at the end of eal init.

Control threads are used to get CPU cycles for telemetry, which are
configured in this patch also.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
config/common_base | 5 ++
lib/Makefile | 2 +
lib/librte_telemetry/Makefile | 28 ++++++
lib/librte_telemetry/meson.build | 7 ++
lib/librte_telemetry/rte_telemetry.c | 118 +++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 36 ++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 32 +++++++
lib/librte_telemetry/rte_telemetry_version.map | 6 ++
lib/meson.build | 2 +-
mk/rte.app.mk | 1 +
10 files changed, 236 insertions(+), 1 deletion(-)
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map

diff --git a/config/common_base b/config/common_base
index acc5211..9f60c0d 100644
--- a/config/common_base
+++ b/config/common_base
@@ -722,6 +722,11 @@ CONFIG_RTE_LIBRTE_HASH=y
CONFIG_RTE_LIBRTE_HASH_DEBUG=n

#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+#
# Compile librte_efd
#
CONFIG_RTE_LIBRTE_EFD=y
diff --git a/lib/Makefile b/lib/Makefile
index 8c83942..2b446c6 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev

ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..0d61361
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+LDLIBS += -lpthread
+LDLIBS += -ljansson
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
new file mode 100644
index 0000000..a57a118
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+#include <rte_param.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = userdata;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_WARN("TELEMETRY could not be initialised");
+ 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);
+}
+
+int32_t
+rte_telemetry_init()
+{
+ 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);
+ pthread_attr_init(&attr);
+ 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;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_register);
+
+static struct rte_param param = {
+ .eal_flag = "--telemetry",
+ .help_text = "Enable telemetry library",
+ .cb = &rte_telemetry_init,
+ .enabled = 0
+};
+
+static void
+rte_telemetry_register(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+
+ rte_param_register(&param);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 0000000..d3b0d8d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * Initialize Telemetry
+ *
+ * @return
+ * 0 on successful initialisation.
+ * @return
+ * -ENOMEM on memory allocation error
+ * @return
+ * -EPERM on unknown error failure
+ * @return
+ * -EALREADY if Telemetry is already initialised.
+ */
+int32_t
+rte_telemetry_init(void);
+
+/**
+ * Clean up and free memory.
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -EPERM on failure
+ */
+int32_t
+rte_telemetry_cleanup(void);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
new file mode 100644
index 0000000..4e810a8
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <rte_log.h>
+
+#ifndef _RTE_TELEMETRY_INTERNAL_H_
+#define _RTE_TELEMETRY_INTERNAL_H_
+
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
+ uint32_t socket_id;
+} telemetry_impl;
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
new file mode 100644
index 0000000..992d227
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.11 {
+ global:
+
+ rte_telemetry_init;
+ local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 3acc67e..b5612ad 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -24,7 +24,7 @@ libraries = [ 'compat', # just a header, used for versioning
# add pkt framework libs which use other libs from above
'port', 'table', 'pipeline',
# flow_classify lib depends on pkt framework table lib
- 'flow_classify', 'bpf']
+ 'flow_classify', 'bpf', 'telemetry']

default_cflags = machine_args
if cc.has_argument('-Wno-format-truncation')
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 32579e4..35594b9 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,6 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
Kevin Laatz
2018-10-10 10:51:28 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to update the statistics in
the metrics library with values from the ethdev stats.

Values need to be updated before they are encoded into a JSON
message and sent to the client that requested them. The JSON encoding
will be added in a subsequent patch.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 134 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
lib/librte_telemetry/rte_telemetry_parser.c | 17 ++++
3 files changed, 155 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index dac8a6f..700798f 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -43,6 +43,78 @@ rte_telemetry_is_port_active(int port_id)
return 0;
}

+static int32_t
+rte_telemetry_update_metrics_ethdev(struct telemetry_impl *telemetry,
+ uint16_t port_id, int reg_start_index)
+{
+ int ret, num_xstats, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_telemetry_is_port_active(port_id);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
+ num_xstats);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ uint64_t xstats_values[num_xstats];
+ for (i = 0; i < num_xstats; i++)
+ xstats_values[i] = eth_xstats[i].value;
+
+ ret = rte_metrics_update_values(port_id, reg_start_index, xstats_values,
+ num_xstats);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not update metrics values");
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ free(eth_xstats);
+ return -1;
+ }
+
+ free(eth_xstats);
+ return 0;
+}
+
int32_t
rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
const char *json_string)
@@ -127,6 +199,68 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
+{
+ int ret, i;
+ char *json_buffer = NULL;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (metric_ids == NULL) {
+ TELEMETRY_LOG_ERR("Invalid metric_ids array");
+ goto einval_fail;
+ }
+
+ if (num_metric_ids < 0) {
+ TELEMETRY_LOG_ERR("Invalid num_metric_ids, must be positive");
+ goto einval_fail;
+ }
+
+ if (port_ids == NULL) {
+ TELEMETRY_LOG_ERR("Invalid port_ids array");
+ goto einval_fail;
+ }
+
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Invalid num_port_ids, must be positive");
+ goto einval_fail;
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]);
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_update_metrics_ethdev(telemetry,
+ port_ids[i], telemetry->reg_index);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Failed to update ethdev metrics");
+ 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_reg_ethdev_to_metrics(uint16_t port_id)
{
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 86a5ba1..0082cb2 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -71,4 +71,8 @@ rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
int32_t
rte_telemetry_is_port_active(int port_id);

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
index 6bc4c6d..084c160 100644
--- a/lib/librte_telemetry/rte_telemetry_parser.c
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -258,6 +258,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
int ret, num_metrics, i, p;
struct rte_metric_name *names;
uint64_t num_port_ids = 0;
+ uint32_t port_ids[RTE_MAX_ETHPORTS];

if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Invalid telemetry argument");
@@ -313,6 +314,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
uint32_t stat_ids[num_metrics];

RTE_ETH_FOREACH_DEV(p) {
+ port_ids[num_port_ids] = p;
num_port_ids++;
}

@@ -337,6 +339,13 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
goto fail;
}

+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_metrics,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Sending ports stats values failed");
+ goto fail;
+ }
+
return 0;

fail:
@@ -428,6 +437,14 @@ rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
return -1;
}
+
+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Sending ports stats values failed");
+ return -1;
+ }
+
return 0;
}
--
2.9.5
Kevin Laatz
2018-10-10 10:51:27 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the parser file. This is used to parse any
messages that are received on any of the client sockets.

Currently, the unregister functionality works using the parser.
Functionality relating to getting statistic values for certain ports
will be added in a subsequent patch, however the parsing involved
for that command is added in this patch.

Some of the parser code included is in preparation for future
functionality, that is not implemented yet in this patchset.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 11 +-
lib/librte_telemetry/rte_telemetry_internal.h | 13 +
lib/librte_telemetry/rte_telemetry_parser.c | 569 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
6 files changed, 608 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index 0d61361..95c7296 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -21,6 +21,7 @@ LIBABIVER := 1

# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 0ccfa36..7450f96 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 22bb554..dac8a6f 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -17,6 +17,7 @@

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
+#include "rte_telemetry_parser.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
@@ -282,6 +283,7 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
static int32_t
rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
{
+ int ret;
telemetry_client *client;
char client_buf[BUF_SIZE];
int bytes;
@@ -290,8 +292,15 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
bytes = read(client->fd, client_buf, BUF_SIZE-1);
client_buf[bytes] = '\0';

- if (bytes > 0)
+ if (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;
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf..86a5ba1 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -43,6 +43,11 @@ typedef struct telemetry_impl {
int register_fail_count;
} telemetry_impl;

+enum rte_telemetry_parser_actions {
+ ACTION_GET = 0,
+ ACTION_DELETE = 2
+};
+
int32_t
rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);

@@ -58,4 +63,12 @@ int32_t
rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
const char *client_path);

+/**
+ * This is a wrapper for the ethdev api rte_eth_find_next().
+ * If rte_eth_find_next() returns the same port id that we passed it,
+ * then we know that that port is active.
+ */
+int32_t
+rte_telemetry_is_port_active(int port_id);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
new file mode 100644
index 0000000..6bc4c6d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -0,0 +1,569 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+
+#include <rte_metrics.h>
+#include <rte_common.h>
+#include <rte_ethdev.h>
+
+#include "rte_telemetry_internal.h"
+
+typedef int (*command_func)(struct telemetry_impl *, int, json_t *);
+
+struct rte_telemetry_command {
+ char *text;
+ command_func fn;
+} command;
+
+static int32_t
+rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ goto einval_fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
+ if (!json_is_string(client_path)) {
+ TELEMETRY_LOG_WARN("Command value is not a string");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_unregister_client(telemetry,
+ json_string_value(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not unregister client");
+ goto einval_fail;
+ }
+
+ 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_command_ports(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'ports' command");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ 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_command_ports_details(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ json_t *value, *port_ids_json = json_object_get(data, "ports");
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ int ret, port_ids[num_port_ids];
+ RTE_SET_USED(port_ids);
+ size_t index;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_array(port_ids_json)) {
+ TELEMETRY_LOG_WARN("Invalid Port ID array");
+ goto einval_fail;
+ }
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Port ID given is invalid");
+ goto einval_fail;
+ }
+ port_ids[index] = json_integer_value(value);
+ }
+
+ 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_command_port_stats(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'port_stats' command");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ 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_stat_names_to_ids(struct telemetry_impl *telemetry,
+ const char * const *stat_names, uint32_t *stat_ids,
+ uint64_t num_stat_names)
+{
+ struct rte_metric_name *names;
+ int ret, num_metrics;
+ uint32_t i, k;
+
+ if (stat_names == NULL) {
+ TELEMETRY_LOG_WARN("Invalid stat_names argument");
+ goto einval_fail;
+ }
+
+ if (num_stat_names <= 0) {
+ TELEMETRY_LOG_WARN("Invalid num_stat_names argument");
+ goto einval_fail;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+ goto eperm_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_WARN("No metrics have been registered");
+ goto eperm_fail;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory for names");
+
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Cannot get metrics names");
+ free(names);
+ goto eperm_fail;
+ }
+
+ k = 0;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ uint32_t j;
+ for (j = 0; j < (uint32_t)num_metrics; j++) {
+ if (strcmp(stat_names[i], names[j].name) == 0) {
+ stat_ids[k] = j;
+ k++;
+ break;
+ }
+ }
+ }
+
+ if (k != num_stat_names) {
+ TELEMETRY_LOG_WARN("Invalid stat names provided");
+ free(names);
+ goto einval_fail;
+ }
+
+ free(names);
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret, num_metrics, i, p;
+ struct rte_metric_name *names;
+ uint64_t num_port_ids = 0;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ const char *stat_names[num_metrics];
+ uint32_t stat_ids[num_metrics];
+
+ RTE_ETH_FOREACH_DEV(p) {
+ num_port_ids++;
+ }
+
+ if (!num_port_ids) {
+ TELEMETRY_LOG_WARN("No active ports");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ goto fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ for (i = 0; i < num_metrics; i++)
+ stat_names[i] = names[i].name;
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_metrics);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ free(names);
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
+ *telemetry, int action, json_t *data)
+{
+ int ret;
+ json_t *port_ids_json = json_object_get(data, "ports");
+ json_t *stat_names_json = json_object_get(data, "stats");
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ uint64_t num_stat_names = json_array_size(stat_names_json);
+ const char *stat_names[num_stat_names];
+ uint32_t port_ids[num_port_ids], stat_ids[num_stat_names];
+ size_t index;
+ json_t *value;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (!json_is_array(port_ids_json) ||
+ !json_is_array(stat_names_json)) {
+ TELEMETRY_LOG_WARN("Invalid input data array(s)");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Port ID given is not valid");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ ret = rte_telemetry_is_port_active(port_ids[index]);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+ }
+
+ json_array_foreach(stat_names_json, index, value) {
+ if (!json_is_string(value)) {
+ TELEMETRY_LOG_WARN("Stat Name given is not a string");
+
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+ stat_names[index] = json_string_value(value);
+ }
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_stat_names);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
+ return -1;
+ }
+ return 0;
+}
+
+static int32_t
+rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
+ const char *command, json_t *data)
+{
+ int ret;
+ uint32_t i;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ struct rte_telemetry_command commands[] = {
+ {
+ .text = "clients",
+ .fn = &rte_telemetry_command_clients
+ },
+ {
+ .text = "ports",
+ .fn = &rte_telemetry_command_ports
+ },
+ {
+ .text = "ports_details",
+ .fn = &rte_telemetry_command_ports_details
+ },
+ {
+ .text = "port_stats",
+ .fn = &rte_telemetry_command_port_stats
+ },
+ {
+ .text = "ports_stats_values_by_name",
+ .fn = &rte_telemetry_command_ports_stats_values_by_name
+ },
+ {
+ .text = "ports_all_stat_values",
+ .fn = &rte_telemetry_command_ports_all_stat_values
+ }
+ };
+
+ const uint32_t num_commands = RTE_DIM(commands);
+
+ for (i = 0; i < num_commands; i++) {
+ if (strcmp(command, commands[i].text) == 0) {
+ ret = commands[i].fn(telemetry, action, data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Command Function for %s failed",
+ commands[i].text);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ TELEMETRY_LOG_WARN("\"%s\" command not found", command);
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+}
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
+{
+ int ret, action_int;
+ json_error_t error;
+ json_t *root, *action, *command, *data;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ root = json_loads(socket_rx_data, 0, &error);
+ if (root == NULL) {
+ TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+ error.text);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -EPERM;
+ } else if (!json_is_object(root)) {
+ TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+ json_decref(root);
+ goto einval_fail;
+ }
+
+ action = json_object_get(root, "action");
+ if (action == NULL) {
+ TELEMETRY_LOG_WARN("Request does not have action field");
+ goto einval_fail;
+ } else if (!json_is_integer(action)) {
+ TELEMETRY_LOG_WARN("Action value is not an integer");
+ goto einval_fail;
+ }
+
+ command = json_object_get(root, "command");
+ if (command == NULL) {
+ TELEMETRY_LOG_WARN("Request does not have command field");
+ goto einval_fail;
+ } else if (!json_is_string(command)) {
+ TELEMETRY_LOG_WARN("Command value is not a string");
+ goto einval_fail;
+ }
+
+ action_int = json_integer_value(action);
+ if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Invalid action code");
+ goto einval_fail;
+ }
+
+ const char *command_string = json_string_value(command);
+ data = json_object_get(root, "data");
+ if (data == NULL) {
+ TELEMETRY_LOG_WARN("Request does not have data field");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
+ data);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse command");
+ return -EINVAL;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -EPERM;
+ }
+ return -EINVAL;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser.h b/lib/librte_telemetry/rte_telemetry_parser.h
new file mode 100644
index 0000000..63e633d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_PARSER_H_
+#define _RTE_TELEMETRY_PARSER_H_
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data);
+
+#endif
--
2.9.5
Kevin Laatz
2018-10-10 10:51:29 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to create a JSON message in
order to send it to a client socket.

When stats are requested by a client, they are retrieved from
the metrics library and encoded in JSON format.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 309 ++++++++++++++++++++++++++++++++++-
1 file changed, 307 insertions(+), 2 deletions(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 700798f..18b8102 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -187,7 +187,7 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return -EPERM;
}

- json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_buffer = json_dumps(root, 0);
json_decref(root);

ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
@@ -199,6 +199,304 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+static int
+rte_telemetry_get_metrics(struct telemetry_impl *telemetry, uint32_t port_id,
+ struct rte_metric_value *metrics, struct rte_metric_name *names,
+ int num_metrics)
+{
+ int ret, num_values;
+
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Invalid metrics count");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+ goto eperm_fail;
+ }
+
+ if (metrics == NULL) {
+ TELEMETRY_LOG_ERR("Metrics must be initialised.");
+ goto einval_fail;
+ }
+
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Names must be initialised.");
+ goto einval_fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Cannot get metrics names");
+ goto eperm_fail;
+ }
+
+ num_values = rte_metrics_get_values(port_id, NULL, 0);
+ ret = rte_metrics_get_values(port_id, metrics, num_values);
+ if (ret < 0 || ret > num_values) {
+ TELEMETRY_LOG_ERR("Cannot get metrics values");
+ goto eperm_fail;
+ }
+
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+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_json_format_stat(struct telemetry_impl *telemetry, json_t *stats,
+ const char *metric_name, uint64_t metric_value)
+{
+ int ret;
+ json_t *stat = json_object();
+
+ if (stat == NULL) {
+ TELEMETRY_LOG_ERR("Could not create stat JSON object");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "name", json_string(metric_name));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat Name field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "value", json_integer(metric_value));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat Value field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(stats, stat);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat cannot be added to stats json array");
+ goto eperm_fail;
+ }
+
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+}
+
+static int32_t
+rte_telemetry_json_format_port(struct telemetry_impl *telemetry,
+ uint32_t port_id, json_t *ports, uint32_t *metric_ids,
+ uint32_t num_metric_ids)
+{
+ struct rte_metric_value *metrics = 0;
+ struct rte_metric_name *names = 0;
+ int num_metrics, ret, err_ret;
+ json_t *port, *stats;
+ uint32_t i;
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+ goto eperm_fail;
+ }
+
+ metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (metrics == NULL || names == NULL) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory");
+ free(metrics);
+ free(names);
+
+ err_ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (err_ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_telemetry_get_metrics(telemetry, port_id, metrics, names,
+ num_metrics);
+ if (ret < 0) {
+ free(metrics);
+ free(names);
+ TELEMETRY_LOG_ERR("rte_telemetry_get_metrics failed");
+ return -1;
+ }
+
+ port = json_object();
+ stats = json_array();
+ if (port == NULL || stats == NULL) {
+ TELEMETRY_LOG_ERR("Could not create port/stats JSON objects");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(port, "port", json_integer(port_id));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Port field cannot be set");
+ goto eperm_fail;
+ }
+
+ for (i = 0; i < num_metric_ids; i++) {
+ int metric_id = metric_ids[i];
+ int metric_index = -1;
+ int metric_name_key = -1;
+ int32_t j;
+ uint64_t metric_value;
+
+ if (metric_id >= num_metrics) {
+ TELEMETRY_LOG_ERR("Metric_id: %d is not valid",
+ metric_id);
+ goto einval_fail;
+ }
+
+ for (j = 0; j < num_metrics; j++) {
+ if (metrics[j].key == metric_id) {
+ metric_name_key = metrics[j].key;
+ metric_index = j;
+ break;
+ }
+ }
+
+ const char *metric_name = names[metric_name_key].name;
+ metric_value = metrics[metric_index].value;
+
+ if (metric_name_key < 0 || metric_index < 0) {
+ TELEMETRY_LOG_ERR("Could not get metric name/index");
+ goto eperm_fail;
+ }
+
+ ret = rte_telemetry_json_format_stat(telemetry, stats,
+ metric_name, metric_value);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Format stat with id: %u failed",
+ metric_id);
+ free(metrics);
+ free(names);
+ return -1;
+ }
+ }
+
+ if (json_array_size(stats) == 0)
+ ret = json_object_set_new(port, "stats", json_null());
+ else
+ ret = json_object_set_new(port, "stats", stats);
+
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stats object cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(ports, port);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Port object cannot be added to ports array");
+ goto eperm_fail;
+ }
+
+ free(metrics);
+ free(names);
+ return 0;
+
+eperm_fail:
+ free(metrics);
+ free(names);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+einval_fail:
+ free(metrics);
+ free(names);
+ 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_encode_json_format(struct telemetry_impl *telemetry,
+ uint32_t *port_ids, uint32_t num_port_ids, uint32_t *metric_ids,
+ uint32_t num_metric_ids, char **json_buffer)
+{
+ int ret;
+ json_t *root, *ports;
+ uint32_t i;
+
+ if (num_port_ids <= 0 || num_metric_ids <= 0) {
+ TELEMETRY_LOG_ERR("Please provide port and metric ids to query");
+ goto einval_fail;
+ }
+
+ ports = json_array();
+ if (ports == NULL) {
+ TELEMETRY_LOG_ERR("Could not create ports JSON array");
+ goto eperm_fail;
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]);
+ goto einval_fail;
+ }
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ ret = rte_telemetry_json_format_port(telemetry, port_ids[i],
+ ports, metric_ids, num_metric_ids);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Format port in JSON failed");
+ return -1;
+ }
+ }
+
+ root = json_object();
+ if (root == NULL) {
+ TELEMETRY_LOG_ERR("Could not create root JSON object");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "status_code",
+ json_string("Status OK: 200"));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Status code field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "data", ports);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Data field cannot be set");
+ goto eperm_fail;
+ }
+
+ *json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_decref(root);
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+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(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
@@ -238,13 +536,20 @@ rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
}

ret = rte_telemetry_update_metrics_ethdev(telemetry,
- port_ids[i], telemetry->reg_index);
+ port_ids[i], telemetry->reg_index);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to update ethdev metrics");
return -1;
}
}

+ ret = rte_telemetry_encode_json_format(telemetry, port_ids,
+ num_port_ids, metric_ids, num_metric_ids, &json_buffer);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON encode function failed");
+ return -1;
+ }
+
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not write to socket");
--
2.9.5
Kevin Laatz
2018-10-10 10:51:31 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to enable/disable the selftest.

This functionality will be extended in future to make the
enabling/disabling more dynamic and remove this 'hardcoded' approach. We
are temporarily using this approach due to the design changes (vdev vs eal)
made to the library.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index d7f0579..8f5fb44 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -656,6 +656,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
uint16_t pid;
int ret;
+ int selftest = 0;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
@@ -668,18 +669,20 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

telemetry->metrics_register_done = 1;
- ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
- telemetry->server_fd);
- if (ret < 0)
- return -1;
+ if (selftest) {
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ 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;
- }
+ 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");
+ TELEMETRY_LOG_INFO("Success - All Parser Tests Passed");
+ }

return 0;
}
--
2.9.5
Kevin Laatz
2018-10-10 10:51:30 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds all tests for the Telemetry API.
The tests added include a parser test, selftest, and socket
messaging tests.

The parser tests pass valid and invalid messages to the parser
to ensure the correct return values are received.
The selftest tests basic functions in the Telemetry API such as
registering, unregistering, and initialisation.
The socket messaging tests pass messages through the socket and
validates the return message, to ensure the Telemetry API is
responding correctly.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 650 ++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 12 +
lib/librte_telemetry/rte_telemetry_internal.h | 3 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 534 ++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 ++
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 ++
lib/librte_telemetry/rte_telemetry_version.map | 1 +
9 files changed, 1278 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index 95c7296..1a05069 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -22,6 +22,7 @@ LIBABIVER := 1
# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser_test.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7450f96..57dd83d 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c', 'rte_telemetry_parser_test.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h', 'rte_telemetry_parser_test.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 18b8102..d7f0579 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -18,16 +18,34 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#include "rte_telemetry_parser.h"
+#include "rte_telemetry_parser_test.h"
+#include "rte_telemetry_socket_tests.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
#define SLEEP_TIME 10

#define DEFAULT_DPDK_PATH "/var/run/dpdk/rte/telemetry"
+#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"

const char *socket_path = DEFAULT_DPDK_PATH;
static telemetry_impl *static_telemetry;

+struct telemetry_message_test {
+ char *test_name;
+ int (*test_func_ptr)(struct telemetry_impl *telemetry, int fd);
+};
+
+struct json_data {
+ char *status_code;
+ char *data;
+ int port;
+ char *stat_name;
+ int stat_value;
+};
+
int32_t
rte_telemetry_is_port_active(int port_id)
{
@@ -637,6 +655,7 @@ static int32_t
rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
uint16_t pid;
+ int ret;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
@@ -649,6 +668,18 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

telemetry->metrics_register_done = 1;
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ 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;
}
@@ -1129,6 +1160,625 @@ rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
return -1;
}

+int32_t
+rte_telemetry_dummy_client_socket(const char *valid_client_path)
+{
+ int sockfd = socket(AF_UNIX, SOCK_STREAM, 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 = 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;
+ struct sockaddr_un addr = {0};
+ struct sockaddr_un addrs = {0};
+ *send_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ *recv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ listen(telemetry->server_fd, 5);
+ addr.sun_family = AF_UNIX;
+ 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;
+ char *status = "Status Error: Invalid Argument 404";
+ 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";
+ 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;
RTE_INIT(rte_telemetry_register);

diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
index d3b0d8d..958723b 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -33,4 +33,16 @@ rte_telemetry_init(void);
int32_t
rte_telemetry_cleanup(void);

+/**
+ * Runs various tests to ensure telemetry initialisation and register/unregister
+ * functions are working correctly.
+ *
+ * @return
+ * 0 on success when all tests have passed
+ * @return
+ * -1 on failure when the test has failed
+ */
+int32_t
+rte_telemetry_selftest(void);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 0082cb2..de7afda 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -75,4 +75,7 @@ int32_t
rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);

+int32_t
+rte_telemetry_socket_messaging_testing(int index, int socket);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.c b/lib/librte_telemetry/rte_telemetry_parser_test.c
new file mode 100644
index 0000000..9bf66a2
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.c
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_tailq.h>
+#include <rte_string_fns.h>
+
+#include "rte_telemetry_parser.h"
+
+enum choices {
+ INV_ACTION_VAL,
+ INV_COMMAND_VAL,
+ INV_DATA_VAL,
+ INV_ACTION_FIELD,
+ INV_COMMAND_FIELD,
+ INV_DATA_FIELD,
+ INV_JSON_FORMAT,
+ VALID_REQ
+};
+
+
+#define TEST_CLIENT "/var/run/dpdk/test_client"
+
+int32_t
+rte_telemetry_create_test_socket(struct telemetry_impl *telemetry,
+ const char *test_client_path)
+{
+ int ret, sockfd;
+ struct sockaddr_un addr = {0};
+ struct telemetry_client *client;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ TELEMETRY_LOG_ERR("Test socket creation failure");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, test_client_path, sizeof(addr.sun_path));
+ unlink(test_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;
+ }
+
+ ret = rte_telemetry_register_client(telemetry, test_client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Register dummy client failed: %i", ret);
+ return -1;
+ }
+
+ ret = accept(sockfd, NULL, NULL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Socket accept failed");
+ return -1;
+ }
+
+ TAILQ_FOREACH(client, &telemetry->client_list_head, client_list)
+ telemetry->request_client = client;
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, json_t **data)
+{
+
+ int ret;
+ json_t *stat_names_json_array = NULL;
+ json_t *port_ids_json_array = NULL;
+ uint32_t i;
+
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Port Ids Count invalid");
+ goto fail;
+ }
+
+ *data = json_object();
+ if (*data == NULL) {
+ TELEMETRY_LOG_ERR("Data json object creation failed");
+ goto fail;
+ }
+
+ port_ids_json_array = json_array();
+ if (port_ids_json_array == NULL) {
+ TELEMETRY_LOG_ERR("port_ids_json_array creation failed");
+ goto fail;
+ }
+
+ for (i = 0; i < (uint32_t)num_port_ids; i++) {
+ ret = json_array_append(port_ids_json_array,
+ json_integer(port_ids[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON array creation failed");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "ports", port_ids_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting 'ports' value in data object failed");
+ goto fail;
+ }
+
+ if (stat_names) {
+ if (num_stat_names < 0) {
+ TELEMETRY_LOG_ERR("Stat Names Count invalid");
+ goto fail;
+ }
+
+ stat_names_json_array = json_array();
+ if (stat_names_json_array == NULL) {
+ TELEMETRY_LOG_ERR("stat_names_json_array creation failed");
+ goto fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ ret = json_array_append(stat_names_json_array,
+ json_string(stat_names[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON array creation failed");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "stats", stat_names_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting 'stats' value in data object failed");
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ if (*data)
+ json_decref(*data);
+ if (stat_names_json_array)
+ json_decref(stat_names_json_array);
+ if (port_ids_json_array)
+ json_decref(port_ids_json_array);
+ return -1;
+}
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, char **request,
+ int inv_choice)
+{
+ int ret;
+ json_t *root = json_object();
+ json_t *data;
+
+ if (root == NULL) {
+ TELEMETRY_LOG_ERR("Could not create root json object");
+ goto fail;
+ }
+
+ if (inv_choice == INV_ACTION_FIELD) {
+ ret = json_object_set_new(root, "ac--on", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid action field in root object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "action", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid action field in root object failed");
+ goto fail;
+ }
+ }
+
+ if (inv_choice == INV_COMMAND_FIELD) {
+ ret = json_object_set_new(root, "co---nd", json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid command field in root object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "command", json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid command field in root object failed");
+ goto fail;
+ }
+ }
+
+ data = json_null();
+ if (client_path) {
+ data = json_object();
+ if (data == NULL) {
+ TELEMETRY_LOG_ERR("Data json object creation failed");
+ goto fail;
+ }
+
+ ret = json_object_set_new(data, "client_path",
+ json_string(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid client_path field in data object failed");
+ goto fail;
+ }
+
+ } else if (port_ids) {
+ ret = rte_telemetry_format_port_stat_ids(port_ids, num_port_ids,
+ stat_names, num_stat_names, &data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Formatting Port/Stat arrays failed");
+ goto fail;
+ }
+
+ }
+
+ if (inv_choice == INV_DATA_FIELD) {
+ ret = json_object_set_new(root, "d--a", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid data field in data object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "data", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid data field in data object failed");
+ goto fail;
+ }
+ }
+
+ *request = json_dumps(root, 0);
+ if (*request == NULL) {
+ TELEMETRY_LOG_ERR("Converting JSON root object to char* failed");
+ goto fail;
+ }
+
+ json_decref(root);
+ return 0;
+
+fail:
+ if (root)
+ json_decref(root);
+ return -1;
+}
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice)
+{
+ int ret;
+ char *request;
+ char *client_path_data = NULL;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command_choice = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path_data = "INVALID_DATA";
+
+ ret = rte_telemetry_create_json_request(action_choice, command_choice,
+ client_path_data, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice)
+{
+ int ret;
+ char *request;
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ char *command = "ports_details";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ port_ids = NULL;
+
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names,
+ int inv_choice)
+{
+ int ret;
+ char *request;
+ char *command = "ports_stats_values_by_name";
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL) {
+ port_ids = NULL;
+ stat_names = NULL;
+ }
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, stat_names, num_stat_names, &request,
+ inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_unreg_request(struct telemetry_impl *telemetry,
+ int action_choice, const char *client_path, int inv_choice)
+{
+ int ret;
+ char *request;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ char *command = "clients";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path = NULL;
+
+ ret = rte_telemetry_create_json_request(action_choice, command,
+ client_path, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry)
+{
+ int ret;
+ const char *client_path = TEST_CLIENT;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ ret = rte_telemetry_create_test_socket(telemetry, client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create test request client socket");
+ return -1;
+ }
+
+ int port_ids[] = {0, 1};
+ int num_port_ids = RTE_DIM(port_ids);
+
+ static const char * const stat_names[] = {"tx_good_packets",
+ "rx_good_packets"};
+ int num_stat_names = RTE_DIM(stat_names);
+
+ static const char * const test_types[] = {
+ "INVALID ACTION VALUE TESTS",
+ "INVALID COMMAND VALUE TESTS",
+ "INVALID DATA VALUE TESTS",
+ "INVALID ACTION FIELD TESTS",
+ "INVALID COMMAND FIELD TESTS",
+ "INVALID DATA FIELD TESTS",
+ "INVALID JSON FORMAT TESTS",
+ "VALID TESTS"
+ };
+
+
+#define NUM_TEST_TYPES (sizeof(test_types)/sizeof(const char * const))
+
+ uint32_t i;
+ for (i = 0; i < NUM_TEST_TYPES; i++) {
+ TELEMETRY_LOG_INFO("%s", test_types[i]);
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "ports", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports test passed");
+
+ ret = rte_telemetry_send_get_ports_details_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports details valid");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports details invalid");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports details test passed");
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "port_stats", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get port stats valid test");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats test passed");
+
+ ret = rte_telemetry_send_stats_values_by_name_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, stat_names,
+ num_stat_names, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats values by name valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats values by name invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats values by name test passed");
+
+ ret = rte_telemetry_send_unreg_request(telemetry, ACTION_DELETE,
+ client_path, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Deregister valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Deregister invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Deregister test passed");
+ }
+
+ return 0;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.h b/lib/librte_telemetry/rte_telemetry_parser_test.h
new file mode 100644
index 0000000..6ada852
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#ifndef _RTE_TELEMETRY_PARSER_TEST_H_
+#define _RTE_TELEMETRY_PARSER_TEST_H_
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry);
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, json_t **data);
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, char **request,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice);
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_unreg_request(int action_choice, const char *client_path,
+ int inv_choice);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_socket_tests.h b/lib/librte_telemetry/rte_telemetry_socket_tests.h
new file mode 100644
index 0000000..db9167c
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_socket_tests.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdbool.h>
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_SOCKET_TESTING_H_
+#define _RTE_TELEMETRY_SOCKET_TESTING_H_
+
+int32_t
+rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry,
+ int fd);
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
+ int send_fd, int recv_fd);
+
+int32_t
+rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
+ int *recv_fd);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
index 992d227..98459fc 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -2,5 +2,6 @@ DPDK_18.11 {
global:

rte_telemetry_init;
+ rte_telemetry_selftest;
local: *;
};
--
2.9.5
Kevin Laatz
2018-10-10 10:51:32 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds all documentation for telemetry.

A description on how to use the Telemetry API with a DPDK
application is given in this document.

It also adds the MAINTAINERS file entry for telemetry.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
MAINTAINERS | 5 +++
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 85 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 91 insertions(+)
create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 84b9ff7..e695a68 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1168,6 +1168,11 @@ F: test/bpf/
F: test/test/test_bpf.c
F: doc/guides/prog_guide/bpf_lib.rst

+Telemetry
+M: Kevin Laatz <***@intel.com>
+F: lib/librte_telemetry/
+F: usertools/dpdk-telemetry-client.py
+F: doc/guides/howto/telemetry.rst

Test Applications
-----------------
diff --git a/doc/guides/howto/index.rst b/doc/guides/howto/index.rst
index e13a090..a642a2b 100644
--- a/doc/guides/howto/index.rst
+++ b/doc/guides/howto/index.rst
@@ -17,3 +17,4 @@ HowTo Guides
virtio_user_for_container_networking
virtio_user_as_exceptional_path
packet_capture_framework
+ telemetry
diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst
new file mode 100644
index 0000000..3fcb061
--- /dev/null
+++ b/doc/guides/howto/telemetry.rst
@@ -0,0 +1,85 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright(c) 2018 Intel Corporation.
+
+DPDK Telemetry API User Guide
+==============================
+
+This document describes how the Data Plane Development Kit(DPDK) Telemetry API
+is used for querying port statistics from incoming traffic.
+
+Introduction
+------------
+
+The ``librte_telemetry`` provides the functionality so that users may query
+metrics from incoming port traffic. The application which initializes packet
+forwarding will act as the server, sending metrics to the requesting application
+which acts as the client.
+
+In DPDK, applications are used to initialize the ``telemetry``. To view incoming
+traffic on featured ports, the application should be run first (ie. after ports
+are configured). Once the application is running, the service assurance agent
+(for example the collectd plugin) should be run to begin querying the API.
+
+A client connects their Service Assurance application to the DPDK application
+via a UNIX socket. Once a connection is established, a client can send JSON
+messages to the DPDK application requesting metrics via another UNIX client.
+This request is then handled and parsed if valid. The response is then
+formatted in JSON and sent back to the requesting client.
+
+Pre-requisites
+~~~~~~~~~~~~~~
+
+* Python ≥ 2.5
+
+* Jansson library for JSON serialization
+
+Test Environment
+----------------
+
+``telemetry`` offers a range of selftests that a client can run within
+the DPDK application.
+
+Selftests are disabled by default. They can be enabled by setting the 'selftest'
+variable to 1 in rte_telemetry_initial_accept().
+
+Note: this 'hardcoded' value is temporary.
+
+Configuration
+-------------
+
+Enable the telemetry API by modifying the following config option before
+building DPDK::
+
+ CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+Note: Meson will pick this up automatically if ``libjansson`` is available.
+
+Running the Application
+-----------------------
+
+The following steps demonstrate how to run the ``telemetry`` API to query all
+statistics on all active ports, using the ``telemetry_client`` python script
+to query.
+Note: This guide assumes packet generation is applicable and the user is
+testing with testpmd as a DPDK primary application to forward packets, although
+any DPDK application is applicable.
+
+#. Launch testpmd as the primary application with ``telemetry``.::
+
+ ./app/testpmd --telemetry
+
+#. Launch the ``telemetry`` python script with a client filepath.::
+
+ python usertools/telemetry_client.py /var/run/some_client
+
+ The client filepath is going to be used to setup our UNIX connection with the
+ DPDK primary application, in this case ``testpmd``
+ This will initialize a menu where a client can proceed to recursively query
+ statistics, request statistics once or unregister the file_path, thus exiting
+ the menu.
+
+#. Send traffic to any or all available ports from a traffic generator.
+ Select a query option(recursive or singular polling).
+ The metrics will then be displayed on the client terminal in JSON format.
+
+#. Once finished, unregister the client using the menu command.
--
2.9.5
Kevin Laatz
2018-10-10 10:51:33 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds a python script which can be used as a demo
client. The script is interactive and will allow the user to
register, request statistics, and unregister.

To run the script, an argument for the client file path must
be passed in: "python telemetry_client.py <file_path>".

This script is useful to see how the Telemetry API for DPDK
is used, and how to make the initial connection.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
usertools/dpdk-telemetry-client.py | 116 +++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 usertools/dpdk-telemetry-client.py

diff --git a/usertools/dpdk-telemetry-client.py b/usertools/dpdk-telemetry-client.py
new file mode 100644
index 0000000..71c6be7
--- /dev/null
+++ b/usertools/dpdk-telemetry-client.py
@@ -0,0 +1,116 @@
+# SPDK-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+import socket
+import os
+import sys
+import time
+
+BUFFER_SIZE = 200000
+
+METRICS_REQ = "{\"action\":0,\"command\":\"ports_all_stat_values\",\"data\":null}"
+API_REG = "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\":\""
+API_UNREG = "{\"action\":2,\"command\":\"clients\",\"data\":{\"client_path\":\""
+DEFAULT_FP = "/var/run/dpdk/default_client"
+
+class Socket:
+
+ def __init__(self):
+ self.send_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.client_fd = None
+
+ def __del__(self):
+ try:
+ self.send_fd.close()
+ self.recv_fd.close()
+ self.client_fd.close()
+ except:
+ print("Error - Sockets could not be closed")
+
+class Client:
+
+ def __init__(self): # Creates a client instance
+ self.socket = Socket()
+ self.file_path = None
+ self.choice = None
+ self.unregistered = 0
+
+ def __del__(self):
+ try:
+ if self.unregistered == 0:
+ self.unregister();
+ except:
+ print("Error - Client could not be destroyed")
+
+ def getFilepath(self, file_path): # Gets arguments from Command-Line and assigns to instance of client
+ self.file_path = file_path
+
+ def register(self): # Connects a client to DPDK-instance
+ if os.path.exists(self.file_path):
+ os.unlink(self.file_path)
+ try:
+ self.socket.recv_fd.bind(self.file_path)
+ except socket.error as msg:
+ print ("Error - Socket binding error: " + str(msg) + "\n")
+ self.socket.recv_fd.settimeout(2)
+ self.socket.send_fd.connect("/var/run/dpdk/rte/telemetry")
+ JSON = (API_REG + self.file_path + "\"}}")
+ self.socket.send_fd.sendall(JSON)
+ self.socket.recv_fd.listen(1)
+ self.socket.client_fd = self.socket.recv_fd.accept()[0]
+
+ def unregister(self): # Unregister a given client
+ self.socket.client_fd.send(API_UNREG + self.file_path + "\"}}")
+ self.socket.client_fd.close()
+
+ def requestMetrics(self): # Requests metrics for given client
+ self.socket.client_fd.send(METRICS_REQ)
+ data = self.socket.client_fd.recv(BUFFER_SIZE)
+ print "\nResponse: \n", str(data)
+
+ def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client
+ print("\nPlease enter the number of times you'd like to continuously request Metrics:")
+ n_requests = int(input("\n:"))
+ print("\033[F") #Removes the user input from screen, cleans it up
+ print("\033[K")
+ for i in range(n_requests):
+ self.requestMetrics()
+ time.sleep(sleep_time)
+
+ def interactiveMenu(self, sleep_time): # Creates Interactive menu within the script
+ while self.choice != 3:
+ print("\nOptions Menu")
+ print("[1] Send for Metrics for all ports")
+ print("[2] Send for Metrics for all ports recursively")
+ print("[3] Unregister client")
+
+ try:
+ self.choice = int(input("\n:"))
+ print("\033[F") #Removes the user input for screen, cleans it up
+ print("\033[K")
+ if self.choice == 1:
+ self.requestMetrics()
+ elif self.choice == 2:
+ self.repeatedlyRequestMetrics(sleep_time)
+ elif self.choice == 3:
+ self.unregister()
+ self.unregistered = 1
+ else:
+ print("Error - Invalid request choice")
+ except:
+ pass
+
+if __name__ == "__main__":
+
+ sleep_time = 1
+ file_path = ""
+ if (len(sys.argv) == 2):
+ file_path = sys.argv[1]
+ else:
+ print("Warning - No filepath passed, using default (" + DEFAULT_FP + ").")
+ file_path = DEFAULT_FP
+ client = Client()
+ client.getFilepath(file_path)
+ client.register()
+ client.interactiveMenu(sleep_time)
--
2.9.5
Kevin Laatz
2018-10-10 10:51:34 UTC
Permalink
This patch adds telemetry as a dependecy to all applications. Without these
changes, the --telemetry flag will not be recognised and applications will
fail to run if they want to enable telemetry.

Signed-off-by: Bruce Richardson <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
app/meson.build | 4 ++--
app/pdump/meson.build | 2 +-
app/proc-info/meson.build | 2 +-
app/test-bbdev/meson.build | 2 +-
app/test-crypto-perf/meson.build | 2 +-
app/test-pmd/meson.build | 2 +-
config/meson.build | 3 +++
lib/meson.build | 1 +
meson.build | 1 +
9 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/app/meson.build b/app/meson.build
index 99e0b93..8e716bf 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -24,7 +24,7 @@ foreach app:apps
# use "deps" for internal DPDK dependencies, and "ext_deps" for
# external package/library requirements
ext_deps = []
- deps = []
+ deps = ['telemetry']

subdir(name)

@@ -38,7 +38,7 @@ foreach app:apps

link_libs = []
if get_option('default_library') == 'static'
- link_libs = dpdk_drivers
+ link_libs = dpdk_static_libraries + dpdk_drivers
endif

if allow_experimental_apis
diff --git a/app/pdump/meson.build b/app/pdump/meson.build
index 988cb4e..116c27f 100644
--- a/app/pdump/meson.build
+++ b/app/pdump/meson.build
@@ -3,4 +3,4 @@

sources = files('main.c')
allow_experimental_apis = true
-deps = ['ethdev', 'kvargs', 'pdump']
+deps += ['ethdev', 'kvargs', 'pdump']
diff --git a/app/proc-info/meson.build b/app/proc-info/meson.build
index 9c148e3..a52b2ee 100644
--- a/app/proc-info/meson.build
+++ b/app/proc-info/meson.build
@@ -3,4 +3,4 @@

sources = files('main.c')
allow_experimental_apis = true
-deps = ['ethdev', 'metrics']
+deps += ['ethdev', 'metrics']
diff --git a/app/test-bbdev/meson.build b/app/test-bbdev/meson.build
index 653907d..eb8cc04 100644
--- a/app/test-bbdev/meson.build
+++ b/app/test-bbdev/meson.build
@@ -6,4 +6,4 @@ sources = files('main.c',
'test_bbdev_perf.c',
'test_bbdev_vector.c')
allow_experimental_apis = true
-deps = ['bbdev', 'bus_vdev']
+deps += ['bbdev', 'bus_vdev']
diff --git a/app/test-crypto-perf/meson.build b/app/test-crypto-perf/meson.build
index eacd7a0..d735b18 100644
--- a/app/test-crypto-perf/meson.build
+++ b/app/test-crypto-perf/meson.build
@@ -12,4 +12,4 @@ sources = files('cperf_ops.c',
'cperf_test_vectors.c',
'cperf_test_verify.c',
'main.c')
-deps = ['cryptodev']
+deps += ['cryptodev']
diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build
index a0b3be0..c24f75f 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -22,7 +22,7 @@ sources = files('cmdline.c',
'testpmd.c',
'txonly.c')

-deps = ['ethdev', 'gro', 'gso', 'cmdline', 'metrics', 'meter', 'bus_pci']
+deps += ['ethdev', 'gro', 'gso', 'cmdline', 'metrics', 'meter', 'bus_pci']
if dpdk_conf.has('RTE_LIBRTE_PDUMP')
deps += 'pdump'
endif
diff --git a/config/meson.build b/config/meson.build
index 6f9228c..275f00b 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -21,6 +21,9 @@ toolchain = cc.get_id()
dpdk_conf.set_quoted('RTE_TOOLCHAIN', toolchain)
dpdk_conf.set('RTE_TOOLCHAIN_' + toolchain.to_upper(), 1)

+add_project_link_arguments('-Wl,--no-as-needed', language: 'c')
+dpdk_extra_ldflags += '-Wl,--no-as-needed'
+
# use pthreads
add_project_link_arguments('-pthread', language: 'c')
dpdk_extra_ldflags += '-pthread'
diff --git a/lib/meson.build b/lib/meson.build
index b5612ad..ef3f3a6 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -127,6 +127,7 @@ foreach l:libraries
dependencies: shared_deps)

dpdk_libraries = [shared_lib] + dpdk_libraries
+ dpdk_static_libraries = [static_lib] + dpdk_static_libraries
endif # sources.length() > 0

set_variable('shared_' + libname, shared_dep)
diff --git a/meson.build b/meson.build
index c9af335..f3bddb2 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,7 @@ project('DPDK', 'C',
cc = meson.get_compiler('c')
dpdk_conf = configuration_data()
dpdk_libraries = []
+dpdk_static_libraries = []
dpdk_drivers = []
dpdk_extra_ldflags = []
--
2.9.5
Kevin Laatz
2018-10-11 16:58:24 UTC
Permalink
This patchset introduces a Telemetry library for DPDK Service Assurance.
This library provides an easy way to query DPDK Ethdev metrics.

The telemetry library provides a method for a service assurance component
to retrieve metrics from a DPDK packet forwarding application.
Communicating from the service assurance component to DPDK is done using a
UNIX domain socket, passing a JSON formatted string. A reply issent (again
a JSON formatted string) of the current DPDK metrics.

The telemetry component makes use of the existing rte_metrics library to
query values. The values to be transmitted via the telemetry infrastructure
must be present in the Metrics library. Currently the ethdev values are
pushed to the metrics library, and the queried from there there is an open
question on how applications would like this to occur. Currently only
ethdev to metrics functionality is implemented, however other subsystems
like crypto, eventdev, keepalive etc can use similar mechanisms.

Exposing DPDK Telemetry via a socket interface enables service assurance
agents like collectd to consume data from DPDK. This is vital for
monitoring, fault-detection, and error reporting. A collectd plugin has
been created to interact with the DPDK Telemetry component, showing how it
can be used in practice. The collectd plugin will be upstreamed to collectd
at a later stage. A small python script is provided in
./usertools/telemetry_client.py to quick-start using DPDK Telemetry.

Note: Despite opterr being set to 0, --telemetry said to be 'unrecognized'
as a startup print. This is a cosmetic issue and will be addressed in the
future.

---
v2:
- Reworked telemetry as part of EAL instead of using vdev (Gaetan)
- Refactored rte_telemetry_command (Gaetan)
- Added MAINTAINERS file entry (Stephen)
- Updated docs to reflect vdev to eal rework
- Removed collectd patch from patchset (Thomas)
- General code clean up from v1 feedback

v3:
- Reworked registering with eal and moved to rte_param (Gaetan)
- Added BSD implementation for rte_param (Gaetan)
- Updated the paths to align with the new runtime file location (Mattias)
- Fixed pointer checks to align with the coding style 1.8.1 (Mattias)
- Added missing decref's and close's (Mattias)
- Fixed runtime issue in Meson (was not recognising flag due to linking)
- More general clean up

v4:
- Added Doxygen comments for rte_param.h (Thomas)
- Made eal_get_runtime_dir a public function to use outside of EAL (Thomas)
- Reworked telemetry to get path using rte_eal_get_runtime_dir (Thomas)
- Fixed checkpatch coding style error

Ciara Power, Brian Archbold and Kevin Laatz (10):
telemetry: initial telemetry infrastructure
telemetry: add initial connection socket
telemetry: add client feature and sockets
telemetry: add parser for client socket messages
telemetry: update metrics before sending stats
telemetry: format json response when sending stats
telemetry: add tests for telemetry api
telemetry: add ability to disable selftest
doc: add telemetry documentation
usertools: add client python script for telemetry

Kevin Laatz (3):
eal: add param register infrastructure
eal: make get runtime dir function public
build: add dependency on telemetry to apps in meson

MAINTAINERS | 5 +
app/meson.build | 4 +-
app/pdump/meson.build | 2 +-
app/proc-info/meson.build | 2 +-
app/test-bbdev/meson.build | 2 +-
app/test-crypto-perf/meson.build | 2 +-
app/test-pmd/meson.build | 2 +-
config/common_base | 5 +
config/meson.build | 3 +
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 85 +
lib/Makefile | 2 +
lib/librte_eal/bsdapp/eal/Makefile | 1 +
lib/librte_eal/bsdapp/eal/eal.c | 20 +-
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_filesystem.h | 14 +-
lib/librte_eal/common/include/rte_eal.h | 9 +
lib/librte_eal/common/include/rte_param.h | 78 +
lib/librte_eal/common/meson.build | 2 +
lib/librte_eal/common/rte_param.c | 47 +
lib/librte_eal/linuxapp/eal/Makefile | 1 +
lib/librte_eal/linuxapp/eal/eal.c | 20 +-
lib/librte_eal/rte_eal_version.map | 2 +
lib/librte_telemetry/Makefile | 30 +
lib/librte_telemetry/meson.build | 9 +
lib/librte_telemetry/rte_telemetry.c | 1811 +++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 48 +
lib/librte_telemetry/rte_telemetry_internal.h | 81 +
lib/librte_telemetry/rte_telemetry_parser.c | 586 +++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 534 ++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 +
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 +
lib/librte_telemetry/rte_telemetry_version.map | 7 +
lib/meson.build | 3 +-
meson.build | 1 +
mk/rte.app.mk | 1 +
usertools/dpdk-telemetry-client.py | 116 ++
38 files changed, 3606 insertions(+), 19 deletions(-)
create mode 100644 doc/guides/howto/telemetry.rst
create mode 100644 lib/librte_eal/common/include/rte_param.h
create mode 100644 lib/librte_eal/common/rte_param.c
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map
create mode 100644 usertools/dpdk-telemetry-client.py
--
2.9.5
Kevin Laatz
2018-10-11 16:58:25 UTC
Permalink
This commit adds infrastructure to EAL that allows an application to
register it's init function with EAL. This allows libraries to be
initialized at the end of EAL init.

This infrastructure allows libraries that depend on EAL to be initialized
as part of EAL init, removing circular dependency issues.

Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_eal/bsdapp/eal/Makefile | 1 +
lib/librte_eal/bsdapp/eal/eal.c | 18 ++++++-
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_param.h | 78 +++++++++++++++++++++++++++++++
lib/librte_eal/common/meson.build | 2 +
lib/librte_eal/common/rte_param.c | 47 +++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 1 +
lib/librte_eal/linuxapp/eal/eal.c | 18 ++++++-
lib/librte_eal/rte_eal_version.map | 1 +
9 files changed, 165 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_param.h
create mode 100644 lib/librte_eal/common/rte_param.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d27da3d..7f4fa7e 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -66,6 +66,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_elem.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_heap.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_mp.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_keepalive.c
+SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_param.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_service.c
SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_reciprocal.c

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index d7ae9d6..27b7afc 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -42,6 +42,7 @@
#include <rte_devargs.h>
#include <rte_version.h>
#include <rte_vfio.h>
+#include <rte_param.h>
#include <rte_atomic.h>
#include <malloc_heap.h>

@@ -414,12 +415,20 @@ eal_parse_args(int argc, char **argv)
argvopt = argv;
optind = 1;
optreset = 1;
+ opterr = 0;

while ((opt = getopt_long(argc, argvopt, eal_short_options,
eal_long_options, &option_index)) != EOF) {

- /* getopt is not happy, stop right now */
+ /*
+ * getopt didn't recognise the option, lets parse the
+ * registered options to see if the flag is valid
+ */
if (opt == '?') {
+ ret = rte_param_parse(argv[optind-1]);
+ if (ret == 0)
+ continue;
+
eal_usage(prgname);
ret = -1;
goto out;
@@ -788,6 +797,13 @@ rte_eal_init(int argc, char **argv)

rte_eal_mcfg_complete();

+ /* Call each registered callback, if enabled */
+ ret = rte_param_init();
+ if (ret < 0) {
+ rte_eal_init_alert("Callback failed\n");
+ return -1;
+ }
+
return fctret;
}

diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca6882..8def95a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -12,6 +12,7 @@ INC += rte_tailq.h rte_interrupts.h rte_alarm.h
INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_bus.h rte_dev.h rte_class.h
+INC += rte_param.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
INC += rte_malloc.h rte_keepalive.h rte_time.h
INC += rte_service.h rte_service_component.h
diff --git a/lib/librte_eal/common/include/rte_param.h b/lib/librte_eal/common/include/rte_param.h
new file mode 100644
index 0000000..edc40bd
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_param.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_PARAM_H__
+#define __INCLUDE_RTE_PARAM_H__
+
+/**
+ * @file
+ *
+ * This API introduces the ability to register callbacks with EAL. When
+ * registering a callback, the application also provides a flag as part of the
+ * struct used to register. If the flag is passed to EAL when ruuning a DPDK
+ * application, the relevant callback will be called at the end of EAL init.
+ * For example, passing --telemetry will make the telemetry init be called at
+ * the end of EAl init.
+ *
+ * The register API can be used to resolve circular dependency issue between
+ * EAL and the library.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_param_cb)(void);
+
+struct rte_param {
+ TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
+ char *eal_flag; /** The EAL flag */
+ char *help_text; /** Help text for the callback */
+ rte_param_cb cb; /** Function pointer of the callback */
+ int enabled; /** Enabled flag, should be 0 by default */
+};
+
+/**
+ * @internal Check if the passed flag is valid
+ *
+ * @param flag
+ * The flag to be parsed
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -1 on fail
+ */
+int
+rte_param_parse(char *flag);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register a function with EAL. Registering the function will enable the
+ * function to be called at the end of EAL init.
+ *
+ * @param reg_param
+ * rte_param structure
+ */
+void __rte_experimental
+rte_param_register(struct rte_param *reg_param);
+
+/**
+ * @internal Iterate through the registered params and init the enabled ones
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -1 on fail
+ */
+int
+rte_param_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index b7fc984..4069e49 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -33,6 +33,7 @@ common_sources = files(
'malloc_mp.c',
'rte_keepalive.c',
'rte_malloc.c',
+ 'rte_param.c',
'rte_reciprocal.c',
'rte_service.c'
)
@@ -70,6 +71,7 @@ common_headers = files(
'include/rte_malloc_heap.h',
'include/rte_memory.h',
'include/rte_memzone.h',
+ 'include/rte_param.h',
'include/rte_pci_dev_feature_defs.h',
'include/rte_pci_dev_features.h',
'include/rte_per_lcore.h',
diff --git a/lib/librte_eal/common/rte_param.c b/lib/librte_eal/common/rte_param.c
new file mode 100644
index 0000000..6a5a0b7
--- /dev/null
+++ b/lib/librte_eal/common/rte_param.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_param.h>
+
+TAILQ_HEAD(rte_param_list, rte_param);
+
+struct rte_param_list rte_param_list =
+ TAILQ_HEAD_INITIALIZER(rte_param_list);
+
+static struct rte_param *param;
+
+int
+rte_param_parse(char *flag)
+{
+ /* Check if the flag is in the registered inits */
+ TAILQ_FOREACH(param, &rte_param_list, next) {
+ if (strcmp(flag, param->eal_flag) == 0) {
+ param->enabled = 1;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void __rte_experimental
+rte_param_register(struct rte_param *reg_param)
+{
+ TAILQ_INSERT_HEAD(&rte_param_list, reg_param, next);
+}
+
+int
+rte_param_init(void)
+{
+ TAILQ_FOREACH(param, &rte_param_list, next) {
+ if (param->enabled)
+ param->cb();
+ }
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 5c16bc4..2bf8b24 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -74,6 +74,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_elem.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_heap.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_mp.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_keepalive.c
+SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_param.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_service.c
SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_reciprocal.c

diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 4a55d3b..e28562b 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -48,6 +48,7 @@
#include <rte_atomic.h>
#include <malloc_heap.h>
#include <rte_vfio.h>
+#include <rte_param.h>

#include "eal_private.h"
#include "eal_thread.h"
@@ -600,12 +601,20 @@ eal_parse_args(int argc, char **argv)

argvopt = argv;
optind = 1;
+ opterr = 0;

while ((opt = getopt_long(argc, argvopt, eal_short_options,
eal_long_options, &option_index)) != EOF) {

- /* getopt is not happy, stop right now */
+ /*
+ * getopt didn't recognise the option, lets parse the
+ * registered options to see if the flag is valid
+ */
if (opt == '?') {
+ ret = rte_param_parse(argv[optind-1]);
+ if (ret == 0)
+ continue;
+
eal_usage(prgname);
ret = -1;
goto out;
@@ -1071,6 +1080,13 @@ rte_eal_init(int argc, char **argv)

rte_eal_mcfg_complete();

+ /* Call each registered callback, if enabled */
+ ret = rte_param_init();
+ if (ret < 0) {
+ rte_eal_init_alert("Callback failed\n");
+ return -1;
+ }
+
return fctret;
}

diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 73282bb..ccfb8a2 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -341,6 +341,7 @@ EXPERIMENTAL {
rte_mp_request_sync;
rte_mp_request_async;
rte_mp_sendmsg;
+ rte_param_register;
rte_service_lcore_attr_get;
rte_service_lcore_attr_reset_all;
rte_service_may_be_active;
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:22 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:58 AM
Subject: [PATCH v4 01/13] eal: add param register infrastructure
This commit adds infrastructure to EAL that allows an application to
register it's init function with EAL. This allows libraries to be
initialized at the end of EAL init.
This infrastructure allows libraries that depend on EAL to be initialized
as part of EAL init, removing circular dependency issues.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:26 UTC
Permalink
This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <***@intel.com>

---
Note: I have added rte_eal_get_runtime_dir() to the 18.11 version in the
.map file instead of the EXPERIMENTAL section as the function already
existed, we just renamed it and made it public.
---
lib/librte_eal/bsdapp/eal/eal.c | 2 +-
lib/librte_eal/common/eal_filesystem.h | 14 +++++++-------
lib/librte_eal/common/include/rte_eal.h | 9 +++++++++
lib/librte_eal/linuxapp/eal/eal.c | 2 +-
lib/librte_eal/rte_eal_version.map | 1 +
5 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 27b7afc..ee94bc1 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -142,7 +142,7 @@ eal_create_runtime_dir(void)
}

const char *
-eal_get_runtime_dir(void)
+rte_eal_get_runtime_dir(void)
{
return runtime_dir;
}
diff --git a/lib/librte_eal/common/eal_filesystem.h b/lib/librte_eal/common/eal_filesystem.h
index de05feb..f82b4fe 100644
--- a/lib/librte_eal/common/eal_filesystem.h
+++ b/lib/librte_eal/common/eal_filesystem.h
@@ -27,7 +27,7 @@ eal_create_runtime_dir(void);

/* returns runtime dir */
const char *
-eal_get_runtime_dir(void);
+rte_eal_get_runtime_dir(void);

#define RUNTIME_CONFIG_FNAME "config"
static inline const char *
@@ -35,7 +35,7 @@ eal_runtime_config_path(void)
{
static char buffer[PATH_MAX]; /* static so auto-zeroed */

- snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(),
+ snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(),
RUNTIME_CONFIG_FNAME);
return buffer;
}
@@ -47,7 +47,7 @@ eal_mp_socket_path(void)
{
static char buffer[PATH_MAX]; /* static so auto-zeroed */

- snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(),
+ snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(),
MP_SOCKET_FNAME);
return buffer;
}
@@ -55,7 +55,7 @@ eal_mp_socket_path(void)
#define FBARRAY_NAME_FMT "%s/fbarray_%s"
static inline const char *
eal_get_fbarray_path(char *buffer, size_t buflen, const char *name) {
- snprintf(buffer, buflen, FBARRAY_NAME_FMT, eal_get_runtime_dir(), name);
+ snprintf(buffer, buflen, FBARRAY_NAME_FMT, rte_eal_get_runtime_dir(), name);
return buffer;
}

@@ -66,7 +66,7 @@ eal_hugepage_info_path(void)
{
static char buffer[PATH_MAX]; /* static so auto-zeroed */

- snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(),
+ snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(),
HUGEPAGE_INFO_FNAME);
return buffer;
}
@@ -78,7 +78,7 @@ eal_hugepage_data_path(void)
{
static char buffer[PATH_MAX]; /* static so auto-zeroed */

- snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(),
+ snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(),
HUGEPAGE_DATA_FNAME);
return buffer;
}
@@ -99,7 +99,7 @@ eal_get_hugefile_path(char *buffer, size_t buflen, const char *hugedir, int f_id
static inline const char *
eal_get_hugefile_lock_path(char *buffer, size_t buflen, int f_id)
{
- snprintf(buffer, buflen, HUGEFILE_LOCK_FMT, eal_get_runtime_dir(),
+ snprintf(buffer, buflen, HUGEFILE_LOCK_FMT, rte_eal_get_runtime_dir(),
f_id);
buffer[buflen - 1] = '\0';
return buffer;
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index e114dcb..e19f74e 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -498,6 +498,15 @@ enum rte_iova_mode rte_eal_iova_mode(void);
const char *
rte_eal_mbuf_user_pool_ops(void);

+/**
+ * Get the runtime directory of DPDK
+ *
+ * @return
+ * The runtime directory path of DPDK
+ */
+const char *
+rte_eal_get_runtime_dir(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index e28562b..e926f67 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -150,7 +150,7 @@ eal_create_runtime_dir(void)
}

const char *
-eal_get_runtime_dir(void)
+rte_eal_get_runtime_dir(void)
{
return runtime_dir;
}
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index ccfb8a2..2de7197 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -265,6 +265,7 @@ DPDK_18.08 {
DPDK_18.11 {
global:

+ rte_eal_get_runtime_dir;
rte_strscpy;

} DPDK_18.08;
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:25 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:58 AM
Subject: [PATCH v4 02/13] eal: make get runtime dir function public
This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.
---
Note: I have added rte_eal_get_runtime_dir() to the 18.11 version in the
.map file instead of the EXPERIMENTAL section as the function already
existed, we just renamed it and made it public.
---
There's a minor rebase on .map file, but that's unavoidable with in-flight patchsets:

Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:27 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the infrastructure and initial code for the telemetry
library.

The telemetry init is registered with eal_init(). We can then check to see
if --telemetry was passed as an eal flag. If --telemetry was parsed, then
we call telemetry init at the end of eal init.

Control threads are used to get CPU cycles for telemetry, which are
configured in this patch also.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
config/common_base | 5 ++
lib/Makefile | 2 +
lib/librte_telemetry/Makefile | 28 ++++++
lib/librte_telemetry/meson.build | 7 ++
lib/librte_telemetry/rte_telemetry.c | 118 +++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 36 ++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 32 +++++++
lib/librte_telemetry/rte_telemetry_version.map | 6 ++
lib/meson.build | 2 +-
mk/rte.app.mk | 1 +
10 files changed, 236 insertions(+), 1 deletion(-)
create mode 100644 lib/librte_telemetry/Makefile
create mode 100644 lib/librte_telemetry/meson.build
create mode 100644 lib/librte_telemetry/rte_telemetry.c
create mode 100644 lib/librte_telemetry/rte_telemetry.h
create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h
create mode 100644 lib/librte_telemetry/rte_telemetry_version.map

diff --git a/config/common_base b/config/common_base
index acc5211..9f60c0d 100644
--- a/config/common_base
+++ b/config/common_base
@@ -722,6 +722,11 @@ CONFIG_RTE_LIBRTE_HASH=y
CONFIG_RTE_LIBRTE_HASH_DEBUG=n

#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+#
# Compile librte_efd
#
CONFIG_RTE_LIBRTE_EFD=y
diff --git a/lib/Makefile b/lib/Makefile
index 8c83942..2b446c6 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net
DEPDIRS-librte_gso += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
+DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev

ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
new file mode 100644
index 0000000..0d61361
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_telemetry.a
+
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDLIBS += -lrte_eal -lrte_ethdev
+LDLIBS += -lrte_metrics
+LDLIBS += -lpthread
+LDLIBS += -ljansson
+
+EXPORT_MAP := rte_telemetry_version.map
+
+LIBABIVER := 1
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+
+# export include files
+SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
new file mode 100644
index 0000000..7716076
--- /dev/null
+++ b/lib/librte_telemetry/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+sources = files('rte_telemetry.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+deps += ['metrics', 'ethdev']
+cflags += '-DALLOW_EXPERIMENTAL_API'
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
new file mode 100644
index 0000000..a57a118
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_metrics.h>
+#include <rte_param.h>
+
+#include "rte_telemetry.h"
+#include "rte_telemetry_internal.h"
+
+#define SLEEP_TIME 10
+
+static telemetry_impl *static_telemetry;
+
+static int32_t
+rte_telemetry_run(void *userdata)
+{
+ struct telemetry_impl *telemetry = userdata;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_WARN("TELEMETRY could not be initialised");
+ 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);
+}
+
+int32_t
+rte_telemetry_init()
+{
+ 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);
+ pthread_attr_init(&attr);
+ 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;
+}
+
+int32_t
+rte_telemetry_cleanup(void)
+{
+ struct telemetry_impl *telemetry = static_telemetry;
+ telemetry->thread_status = 0;
+ pthread_join(telemetry->thread_id, NULL);
+ free(telemetry);
+ static_telemetry = NULL;
+ return 0;
+}
+
+int telemetry_log_level;
+RTE_INIT(rte_telemetry_register);
+
+static struct rte_param param = {
+ .eal_flag = "--telemetry",
+ .help_text = "Enable telemetry library",
+ .cb = &rte_telemetry_init,
+ .enabled = 0
+};
+
+static void
+rte_telemetry_register(void)
+{
+ telemetry_log_level = rte_log_register("lib.telemetry");
+ if (telemetry_log_level >= 0)
+ rte_log_set_level(telemetry_log_level, RTE_LOG_ERR);
+
+ rte_param_register(&param);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 0000000..d3b0d8d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * Initialize Telemetry
+ *
+ * @return
+ * 0 on successful initialisation.
+ * @return
+ * -ENOMEM on memory allocation error
+ * @return
+ * -EPERM on unknown error failure
+ * @return
+ * -EALREADY if Telemetry is already initialised.
+ */
+int32_t
+rte_telemetry_init(void);
+
+/**
+ * Clean up and free memory.
+ *
+ * @return
+ * 0 on success
+ * @return
+ * -EPERM on failure
+ */
+int32_t
+rte_telemetry_cleanup(void);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
new file mode 100644
index 0000000..4e810a8
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <rte_log.h>
+
+#ifndef _RTE_TELEMETRY_INTERNAL_H_
+#define _RTE_TELEMETRY_INTERNAL_H_
+
+/* Logging Macros */
+extern int telemetry_log_level;
+
+#define TELEMETRY_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \
+ __func__, ##args)
+
+#define TELEMETRY_LOG_ERR(fmt, args...) \
+ TELEMETRY_LOG(ERR, fmt, ## args)
+
+#define TELEMETRY_LOG_WARN(fmt, args...) \
+ TELEMETRY_LOG(WARNING, fmt, ## args)
+
+#define TELEMETRY_LOG_INFO(fmt, args...) \
+ TELEMETRY_LOG(INFO, fmt, ## args)
+
+typedef struct telemetry_impl {
+ pthread_t thread_id;
+ int thread_status;
+ uint32_t socket_id;
+} telemetry_impl;
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
new file mode 100644
index 0000000..992d227
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,6 @@
+DPDK_18.11 {
+ global:
+
+ rte_telemetry_init;
+ local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 3acc67e..b5612ad 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -24,7 +24,7 @@ libraries = [ 'compat', # just a header, used for versioning
# add pkt framework libs which use other libs from above
'port', 'table', 'pipeline',
# flow_classify lib depends on pkt framework table lib
- 'flow_classify', 'bpf']
+ 'flow_classify', 'bpf', 'telemetry']

default_cflags = machine_args
if cc.has_argument('-Wno-format-truncation')
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 32579e4..35594b9 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,6 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:31 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:58 AM
Subject: [PATCH v4 03/13] telemetry: initial telemetry infrastructure
This patch adds the infrastructure and initial code for the telemetry
library.
The telemetry init is registered with eal_init(). We can then check to see
if --telemetry was passed as an eal flag. If --telemetry was parsed, then
we call telemetry init at the end of eal init.
Control threads are used to get CPU cycles for telemetry, which are
configured in this patch also.
---
I think we need to set the Makefile default to =N, and distros/users can enable
if they have Jansson available. With that update:

Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:28 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.

On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 225 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
2 files changed, 229 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index a57a118..4d23f8e 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,12 +3,16 @@
*/

#include <unistd.h>
+#include <fcntl.h>
#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_metrics.h>
#include <rte_param.h>
+#include <rte_string_fns.h>

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
@@ -17,9 +21,144 @@

static telemetry_impl *static_telemetry;

+static void
+rte_telemetry_get_runtime_dir(char *socket_path, size_t size)
+{
+ snprintf(socket_path, size, "%s/telemetry", rte_eal_get_runtime_dir());
+}
+
+int32_t
+rte_telemetry_is_port_active(int port_id)
+{
+ int ret;
+
+ ret = rte_eth_find_next(port_id);
+ if (ret == port_id)
+ return 1;
+
+ TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
+ port_id);
+ return 0;
+}
+
+static int32_t
+rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
+{
+ int ret, num_xstats, ret_val, i;
+ struct rte_eth_xstat *eth_xstats = NULL;
+ struct rte_eth_xstat_name *eth_xstats_names = NULL;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ return -EINVAL;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
+ port_id, num_xstats);
+ return -EPERM;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ return -ENOMEM;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ const char *xstats_names[num_xstats];
+ eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ if (eth_xstats_names == NULL) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats_names");
+ ret_val = -ENOMEM;
+ goto free_xstats;
+ }
+
+ ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret_val = -EPERM;
+ goto free_xstats;
+ }
+
+ for (i = 0; i < num_xstats; i++)
+ xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name;
+
+ ret_val = rte_metrics_reg_names(xstats_names, num_xstats);
+ if (ret_val < 0) {
+ TELEMETRY_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
+ ret_val = -1;
+ goto free_xstats;
+ }
+
+ goto free_xstats;
+
+free_xstats:
+ free(eth_xstats);
+ free(eth_xstats_names);
+ return ret_val;
+}
+
+static int32_t
+rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
+{
+ uint16_t pid;
+
+ RTE_ETH_FOREACH_DEV(pid) {
+ telemetry->reg_index =
+ rte_telemetry_reg_ethdev_to_metrics(pid);
+ break;
+ }
+
+ if (telemetry->reg_index < 0) {
+ TELEMETRY_LOG_ERR("Failed to register ethdev metrics");
+ return -1;
+ }
+
+ telemetry->metrics_register_done = 1;
+
+ return 0;
+}
+
+static int32_t
+rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
+{
+ int ret;
+
+ if (telemetry->accept_fd == 0 || telemetry->accept_fd == -1) {
+ 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;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int32_t
rte_telemetry_run(void *userdata)
{
+ int ret;
struct telemetry_impl *telemetry = userdata;

if (telemetry == NULL) {
@@ -27,6 +166,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_accept_new_client(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Accept and read new client failed");
+ return -1;
+ }
+
return 0;
}

@@ -50,6 +195,68 @@ static void
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 = {0};
+ char socket_path[BUF_SIZE];
+
+ if (telemetry == NULL)
+ return -1;
+
+ telemetry->server_fd = socket(AF_UNIX, SOCK_STREAM, 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");
+ free(telemetry);
+ return -EPERM;
+ }
+
+ return -1;
+}
+
int32_t
rte_telemetry_init()
{
@@ -70,6 +277,14 @@ rte_telemetry_init()

static_telemetry->socket_id = rte_socket_id();
rte_metrics_init(static_telemetry->socket_id);
+ 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;
+ }
+
pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -89,11 +304,21 @@ rte_telemetry_init()
int32_t
rte_telemetry_cleanup(void)
{
+ int ret;
struct telemetry_impl *telemetry = static_telemetry;
+
+ 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;
}

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 4e810a8..569d56a 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -24,9 +24,13 @@ extern int telemetry_log_level;
TELEMETRY_LOG(INFO, fmt, ## args)

typedef struct telemetry_impl {
+ int accept_fd;
+ int server_fd;
pthread_t thread_id;
int thread_status;
uint32_t socket_id;
+ int reg_index;
+ int metrics_register_done;
} telemetry_impl;

#endif
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:37 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:58 AM
Subject: [PATCH v4 04/13] telemetry: add initial connection socket
This patch adds the telemetry UNIX socket. It is used to
allow connections from external clients.
On the initial connection from a client, ethdev stats are
registered in the metrics library, to allow for their retrieval
at a later stage.
---
<big snip>
+static int32_t
+rte_telemetry_create_socket(struct telemetry_impl *telemetry)
+{
+ int ret;
+ struct sockaddr_un addr = {0};
+ char socket_path[BUF_SIZE];
BUF_SIZE is not yet defined - it is in the next patch.
Pull the #define back into this patchset; now this patch
(without next one applied) won't build. With that fix;

Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:29 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch introduces clients to the telemetry API.

When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.

A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.

Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/meson.build | 2 +
lib/librte_telemetry/rte_telemetry.c | 370 +++++++++++++++++++++++++-
lib/librte_telemetry/rte_telemetry_internal.h | 25 ++
mk/rte.app.mk | 2 +-
4 files changed, 395 insertions(+), 4 deletions(-)

diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076..0ccfa36 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_telemetry.c')
headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
+jansson = cc.find_library('jansson', required: true)
+ext_deps += jansson
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 4d23f8e..d019c9e 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -7,6 +7,7 @@
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <jansson.h>

#include <rte_eal.h>
#include <rte_ethdev.h>
@@ -17,6 +18,8 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"

+#define BUF_SIZE 1024
+#define ACTION_POST 1
#define SLEEP_TIME 10

static telemetry_impl *static_telemetry;
@@ -38,6 +41,91 @@ rte_telemetry_is_port_active(int port_id)

TELEMETRY_LOG_ERR("port_id: %d is invalid, not active",
port_id);
+
+ return 0;
+}
+
+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, JSON_INDENT(2));
+ 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;
}

@@ -114,8 +202,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
uint16_t pid;

RTE_ETH_FOREACH_DEV(pid) {
- telemetry->reg_index =
- rte_telemetry_reg_ethdev_to_metrics(pid);
+ telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
break;
}

@@ -130,6 +217,40 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+ char buf[BUF_SIZE];
+ int ret, buffer_read = 0;
+
+ buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+ buf[buffer_read] = '\0';
+
+ if (buffer_read == -1) {
+ TELEMETRY_LOG_ERR("Read error");
+ return -1;
+ } else if (buffer_read == 0) {
+ goto close_socket;
+ } else {
+ ret = rte_telemetry_parse_client_message(telemetry, buf);
+ if (ret < 0)
+ TELEMETRY_LOG_WARN("Parse message failed");
+ goto close_socket;
+ }
+
+ return 0;
+
+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;
@@ -140,8 +261,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
TELEMETRY_LOG_ERR("Listening error with server fd");
return -1;
}
- telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);

+ 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);
@@ -150,6 +271,30 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
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)
+{
+ 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);
+ client_buf[bytes] = '\0';
+
+ if (bytes > 0)
+ telemetry->request_client = client;
}

return 0;
@@ -172,6 +317,12 @@ rte_telemetry_run(void *userdata)
return -1;
}

+ ret = rte_telemetry_read_client_sockets(telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Client socket read failed");
+ return -1;
+ }
+
return 0;
}

@@ -284,6 +435,7 @@ rte_telemetry_init()
TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
return -EPERM;
}
+ TAILQ_INIT(&static_telemetry->client_list_head);

pthread_attr_init(&attr);
ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
@@ -301,11 +453,39 @@ rte_telemetry_init()
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) {
@@ -322,6 +502,190 @@ rte_telemetry_cleanup(void)
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 = {0};
+
+ 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_STREAM, 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");
+ json_decref(root);
+ 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");
+ return -1;
+}
+
int telemetry_log_level;
RTE_INIT(rte_telemetry_register);

diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 569d56a..e3292cf 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -3,6 +3,7 @@
*/

#include <rte_log.h>
+#include <rte_tailq.h>

#ifndef _RTE_TELEMETRY_INTERNAL_H_
#define _RTE_TELEMETRY_INTERNAL_H_
@@ -23,6 +24,12 @@ extern int telemetry_log_level;
#define TELEMETRY_LOG_INFO(fmt, args...) \
TELEMETRY_LOG(INFO, fmt, ## args)

+typedef struct telemetry_client {
+ char *file_path;
+ int fd;
+ TAILQ_ENTRY(telemetry_client) client_list;
+} telemetry_client;
+
typedef struct telemetry_impl {
int accept_fd;
int server_fd;
@@ -31,6 +38,24 @@ typedef struct telemetry_impl {
uint32_t socket_id;
int reg_index;
int metrics_register_done;
+ TAILQ_HEAD(, telemetry_client) client_list_head;
+ struct telemetry_client *request_client;
+ int register_fail_count;
} telemetry_impl;

+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);
+
+int32_t
+rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
+ int error_type);
+
+int32_t
+rte_telemetry_register_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
+int32_t
+rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
+ const char *client_path);
+
#endif
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 35594b9..b740691 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security
_LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev
_LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev
-_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_metrics -lrte_telemetry -ljansson
_LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer
_LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool
_LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:41 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:58 AM
Subject: [PATCH v4 05/13] telemetry: add client feature and sockets
This patch introduces clients to the telemetry API.
When a client makes a connection through the initial telemetry
socket, they can send a message through the socket to be
parsed. Register messages are expected through this socket, to
enable clients to register and have a client socket setup for
future communications.
A TAILQ is used to store all clients information. Using this, the
client sockets are polled for messages, which will later be parsed
and dealt with accordingly.
Functionality that make use of the client sockets were introduced
in this patch also, such as writing to client sockets, and sending
error responses.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:30 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds the parser file. This is used to parse any
messages that are received on any of the client sockets.

Currently, the unregister functionality works using the parser.
Functionality relating to getting statistic values for certain ports
will be added in a subsequent patch, however the parsing involved
for that command is added in this patch.

Some of the parser code included is in preparation for future
functionality, that is not implemented yet in this patchset.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 11 +-
lib/librte_telemetry/rte_telemetry_internal.h | 13 +
lib/librte_telemetry/rte_telemetry_parser.c | 569 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser.h | 13 +
6 files changed, 608 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index 0d61361..95c7296 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -21,6 +21,7 @@ LIBABIVER := 1

# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 0ccfa36..7450f96 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index d019c9e..479479c 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -17,6 +17,7 @@

#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
+#include "rte_telemetry_parser.h"

#define BUF_SIZE 1024
#define ACTION_POST 1
@@ -285,6 +286,7 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
static int32_t
rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
{
+ int ret;
telemetry_client *client;
char client_buf[BUF_SIZE];
int bytes;
@@ -293,8 +295,15 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
bytes = read(client->fd, client_buf, BUF_SIZE-1);
client_buf[bytes] = '\0';

- if (bytes > 0)
+ if (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;
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf..86a5ba1 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -43,6 +43,11 @@ typedef struct telemetry_impl {
int register_fail_count;
} telemetry_impl;

+enum rte_telemetry_parser_actions {
+ ACTION_GET = 0,
+ ACTION_DELETE = 2
+};
+
int32_t
rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf);

@@ -58,4 +63,12 @@ int32_t
rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
const char *client_path);

+/**
+ * This is a wrapper for the ethdev api rte_eth_find_next().
+ * If rte_eth_find_next() returns the same port id that we passed it,
+ * then we know that that port is active.
+ */
+int32_t
+rte_telemetry_is_port_active(int port_id);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
new file mode 100644
index 0000000..6bc4c6d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -0,0 +1,569 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+
+#include <rte_metrics.h>
+#include <rte_common.h>
+#include <rte_ethdev.h>
+
+#include "rte_telemetry_internal.h"
+
+typedef int (*command_func)(struct telemetry_impl *, int, json_t *);
+
+struct rte_telemetry_command {
+ char *text;
+ command_func fn;
+} command;
+
+static int32_t
+rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ goto einval_fail;
+ }
+
+ json_t *client_path = json_object_get(data, "client_path");
+ if (!json_is_string(client_path)) {
+ TELEMETRY_LOG_WARN("Command value is not a string");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_unregister_client(telemetry,
+ json_string_value(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not unregister client");
+ goto einval_fail;
+ }
+
+ 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_command_ports(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'ports' command");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ 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_command_ports_details(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ json_t *value, *port_ids_json = json_object_get(data, "ports");
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ int ret, port_ids[num_port_ids];
+ RTE_SET_USED(port_ids);
+ size_t index;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ goto einval_fail;
+ }
+
+ if (!json_is_array(port_ids_json)) {
+ TELEMETRY_LOG_WARN("Invalid Port ID array");
+ goto einval_fail;
+ }
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Port ID given is invalid");
+ goto einval_fail;
+ }
+ port_ids[index] = json_integer_value(value);
+ }
+
+ 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_command_port_stats(struct telemetry_impl *telemetry, int action,
+ json_t *data)
+{
+ int ret;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (!json_is_null(data)) {
+ TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'port_stats' command");
+ goto einval_fail;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ goto einval_fail;
+ }
+
+ 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_stat_names_to_ids(struct telemetry_impl *telemetry,
+ const char * const *stat_names, uint32_t *stat_ids,
+ uint64_t num_stat_names)
+{
+ struct rte_metric_name *names;
+ int ret, num_metrics;
+ uint32_t i, k;
+
+ if (stat_names == NULL) {
+ TELEMETRY_LOG_WARN("Invalid stat_names argument");
+ goto einval_fail;
+ }
+
+ if (num_stat_names <= 0) {
+ TELEMETRY_LOG_WARN("Invalid num_stat_names argument");
+ goto einval_fail;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+ goto eperm_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_WARN("No metrics have been registered");
+ goto eperm_fail;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory for names");
+
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Cannot get metrics names");
+ free(names);
+ goto eperm_fail;
+ }
+
+ k = 0;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ uint32_t j;
+ for (j = 0; j < (uint32_t)num_metrics; j++) {
+ if (strcmp(stat_names[i], names[j].name) == 0) {
+ stat_ids[k] = j;
+ k++;
+ break;
+ }
+ }
+ }
+
+ if (k != num_stat_names) {
+ TELEMETRY_LOG_WARN("Invalid stat names provided");
+ free(names);
+ goto einval_fail;
+ }
+
+ free(names);
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
+ int action, json_t *data)
+{
+ int ret, num_metrics, i, p;
+ struct rte_metric_name *names;
+ uint64_t num_port_ids = 0;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ const char *stat_names[num_metrics];
+ uint32_t stat_ids[num_metrics];
+
+ RTE_ETH_FOREACH_DEV(p) {
+ num_port_ids++;
+ }
+
+ if (!num_port_ids) {
+ TELEMETRY_LOG_WARN("No active ports");
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ goto fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ for (i = 0; i < num_metrics; i++)
+ stat_names[i] = names[i].name;
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_metrics);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ free(names);
+ return -1;
+}
+
+int32_t
+rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
+ *telemetry, int action, json_t *data)
+{
+ int ret;
+ json_t *port_ids_json = json_object_get(data, "ports");
+ json_t *stat_names_json = json_object_get(data, "stats");
+ uint64_t num_port_ids = json_array_size(port_ids_json);
+ uint64_t num_stat_names = json_array_size(stat_names_json);
+ const char *stat_names[num_stat_names];
+ uint32_t port_ids[num_port_ids], stat_ids[num_stat_names];
+ size_t index;
+ json_t *value;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (action != ACTION_GET) {
+ TELEMETRY_LOG_WARN("Invalid action for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (!json_is_object(data)) {
+ TELEMETRY_LOG_WARN("Invalid data provided for this command");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ if (!json_is_array(port_ids_json) ||
+ !json_is_array(stat_names_json)) {
+ TELEMETRY_LOG_WARN("Invalid input data array(s)");
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ json_array_foreach(port_ids_json, index, value) {
+ if (!json_is_integer(value)) {
+ TELEMETRY_LOG_WARN("Port ID given is not valid");
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+ port_ids[index] = json_integer_value(value);
+ ret = rte_telemetry_is_port_active(port_ids[index]);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+ }
+
+ json_array_foreach(stat_names_json, index, value) {
+ if (!json_is_string(value)) {
+ TELEMETRY_LOG_WARN("Stat Name given is not a string");
+
+ ret = rte_telemetry_send_error_response(telemetry,
+ -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+ }
+ stat_names[index] = json_string_value(value);
+ }
+
+ ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
+ num_stat_names);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
+ return -1;
+ }
+ return 0;
+}
+
+static int32_t
+rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
+ const char *command, json_t *data)
+{
+ int ret;
+ uint32_t i;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ struct rte_telemetry_command commands[] = {
+ {
+ .text = "clients",
+ .fn = &rte_telemetry_command_clients
+ },
+ {
+ .text = "ports",
+ .fn = &rte_telemetry_command_ports
+ },
+ {
+ .text = "ports_details",
+ .fn = &rte_telemetry_command_ports_details
+ },
+ {
+ .text = "port_stats",
+ .fn = &rte_telemetry_command_port_stats
+ },
+ {
+ .text = "ports_stats_values_by_name",
+ .fn = &rte_telemetry_command_ports_stats_values_by_name
+ },
+ {
+ .text = "ports_all_stat_values",
+ .fn = &rte_telemetry_command_ports_all_stat_values
+ }
+ };
+
+ const uint32_t num_commands = RTE_DIM(commands);
+
+ for (i = 0; i < num_commands; i++) {
+ if (strcmp(command, commands[i].text) == 0) {
+ ret = commands[i].fn(telemetry, action, data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Command Function for %s failed",
+ commands[i].text);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ TELEMETRY_LOG_WARN("\"%s\" command not found", command);
+
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+
+ return -1;
+}
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
+{
+ int ret, action_int;
+ json_error_t error;
+ json_t *root, *action, *command, *data;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ root = json_loads(socket_rx_data, 0, &error);
+ if (root == NULL) {
+ TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+ error.text);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -EPERM;
+ } else if (!json_is_object(root)) {
+ TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+ json_decref(root);
+ goto einval_fail;
+ }
+
+ action = json_object_get(root, "action");
+ if (action == NULL) {
+ TELEMETRY_LOG_WARN("Request does not have action field");
+ goto einval_fail;
+ } else if (!json_is_integer(action)) {
+ TELEMETRY_LOG_WARN("Action value is not an integer");
+ goto einval_fail;
+ }
+
+ command = json_object_get(root, "command");
+ if (command == NULL) {
+ TELEMETRY_LOG_WARN("Request does not have command field");
+ goto einval_fail;
+ } else if (!json_is_string(command)) {
+ TELEMETRY_LOG_WARN("Command value is not a string");
+ goto einval_fail;
+ }
+
+ action_int = json_integer_value(action);
+ if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
+ TELEMETRY_LOG_WARN("Invalid action code");
+ goto einval_fail;
+ }
+
+ const char *command_string = json_string_value(command);
+ data = json_object_get(root, "data");
+ if (data == NULL) {
+ TELEMETRY_LOG_WARN("Request does not have data field");
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
+ data);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse command");
+ return -EINVAL;
+ }
+
+ return 0;
+
+einval_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -EPERM;
+ }
+ return -EINVAL;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser.h b/lib/librte_telemetry/rte_telemetry_parser.h
new file mode 100644
index 0000000..63e633d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_PARSER_H_
+#define _RTE_TELEMETRY_PARSER_H_
+
+int32_t
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data);
+
+#endif
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:43 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 06/13] telemetry: add parser for client socket messages
This patch adds the parser file. This is used to parse any
messages that are received on any of the client sockets.
Currently, the unregister functionality works using the parser.
Functionality relating to getting statistic values for certain ports
will be added in a subsequent patch, however the parsing involved
for that command is added in this patch.
Some of the parser code included is in preparation for future
functionality, that is not implemented yet in this patchset.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:31 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to update the statistics in
the metrics library with values from the ethdev stats.

Values need to be updated before they are encoded into a JSON
message and sent to the client that requested them. The JSON encoding
will be added in a subsequent patch.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 134 ++++++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry_internal.h | 4 +
lib/librte_telemetry/rte_telemetry_parser.c | 17 ++++
3 files changed, 155 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 479479c..947f267 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -46,6 +46,78 @@ rte_telemetry_is_port_active(int port_id)
return 0;
}

+static int32_t
+rte_telemetry_update_metrics_ethdev(struct telemetry_impl *telemetry,
+ uint16_t port_id, int reg_start_index)
+{
+ int ret, num_xstats, i;
+ struct rte_eth_xstat *eth_xstats;
+
+ if (!rte_eth_dev_is_valid_port(port_id)) {
+ TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id);
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_telemetry_is_port_active(port_id);
+ if (ret < 1) {
+ ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
+ if (num_xstats < 0) {
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
+ num_xstats);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
+ if (eth_xstats == NULL) {
+ TELEMETRY_LOG_ERR("Failed to malloc memory for xstats");
+ ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
+ if (ret < 0 || ret > num_xstats) {
+ free(eth_xstats);
+ TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
+ port_id, num_xstats, ret);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ uint64_t xstats_values[num_xstats];
+ for (i = 0; i < num_xstats; i++)
+ xstats_values[i] = eth_xstats[i].value;
+
+ ret = rte_metrics_update_values(port_id, reg_start_index, xstats_values,
+ num_xstats);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not update metrics values");
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ free(eth_xstats);
+ return -1;
+ }
+
+ free(eth_xstats);
+ return 0;
+}
+
int32_t
rte_telemetry_write_to_socket(struct telemetry_impl *telemetry,
const char *json_string)
@@ -130,6 +202,68 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
+{
+ int ret, i;
+ char *json_buffer = NULL;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Invalid telemetry argument");
+ return -1;
+ }
+
+ if (metric_ids == NULL) {
+ TELEMETRY_LOG_ERR("Invalid metric_ids array");
+ goto einval_fail;
+ }
+
+ if (num_metric_ids < 0) {
+ TELEMETRY_LOG_ERR("Invalid num_metric_ids, must be positive");
+ goto einval_fail;
+ }
+
+ if (port_ids == NULL) {
+ TELEMETRY_LOG_ERR("Invalid port_ids array");
+ goto einval_fail;
+ }
+
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Invalid num_port_ids, must be positive");
+ goto einval_fail;
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]);
+ goto einval_fail;
+ }
+
+ ret = rte_telemetry_update_metrics_ethdev(telemetry,
+ port_ids[i], telemetry->reg_index);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Failed to update ethdev metrics");
+ 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_reg_ethdev_to_metrics(uint16_t port_id)
{
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 86a5ba1..0082cb2 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -71,4 +71,8 @@ rte_telemetry_unregister_client(struct telemetry_impl *telemetry,
int32_t
rte_telemetry_is_port_active(int port_id);

+int32_t
+rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
+ uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c
index 6bc4c6d..084c160 100644
--- a/lib/librte_telemetry/rte_telemetry_parser.c
+++ b/lib/librte_telemetry/rte_telemetry_parser.c
@@ -258,6 +258,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
int ret, num_metrics, i, p;
struct rte_metric_name *names;
uint64_t num_port_ids = 0;
+ uint32_t port_ids[RTE_MAX_ETHPORTS];

if (telemetry == NULL) {
TELEMETRY_LOG_ERR("Invalid telemetry argument");
@@ -313,6 +314,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
uint32_t stat_ids[num_metrics];

RTE_ETH_FOREACH_DEV(p) {
+ port_ids[num_port_ids] = p;
num_port_ids++;
}

@@ -337,6 +339,13 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
goto fail;
}

+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_metrics,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Sending ports stats values failed");
+ goto fail;
+ }
+
return 0;

fail:
@@ -428,6 +437,14 @@ rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
return -1;
}
+
+ ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names,
+ port_ids, num_port_ids, telemetry);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Sending ports stats values failed");
+ return -1;
+ }
+
return 0;
}
--
2.9.5
Van Haaren, Harry
2018-10-16 00:45:59 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 07/13] telemetry: update metrics before sending stats
This patch adds functionality to update the statistics in
the metrics library with values from the ethdev stats.
Values need to be updated before they are encoded into a JSON
message and sent to the client that requested them. The JSON encoding
will be added in a subsequent patch.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:32 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to create a JSON message in
order to send it to a client socket.

When stats are requested by a client, they are retrieved from
the metrics library and encoded in JSON format.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 309 ++++++++++++++++++++++++++++++++++-
1 file changed, 307 insertions(+), 2 deletions(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 947f267..5f59f16 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -190,7 +190,7 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return -EPERM;
}

- json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_buffer = json_dumps(root, 0);
json_decref(root);

ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
@@ -202,6 +202,304 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry,
return 0;
}

+static int
+rte_telemetry_get_metrics(struct telemetry_impl *telemetry, uint32_t port_id,
+ struct rte_metric_value *metrics, struct rte_metric_name *names,
+ int num_metrics)
+{
+ int ret, num_values;
+
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Invalid metrics count");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+ goto eperm_fail;
+ }
+
+ if (metrics == NULL) {
+ TELEMETRY_LOG_ERR("Metrics must be initialised.");
+ goto einval_fail;
+ }
+
+ if (names == NULL) {
+ TELEMETRY_LOG_ERR("Names must be initialised.");
+ goto einval_fail;
+ }
+
+ ret = rte_metrics_get_names(names, num_metrics);
+ if (ret < 0 || ret > num_metrics) {
+ TELEMETRY_LOG_ERR("Cannot get metrics names");
+ goto eperm_fail;
+ }
+
+ num_values = rte_metrics_get_values(port_id, NULL, 0);
+ ret = rte_metrics_get_values(port_id, metrics, num_values);
+ if (ret < 0 || ret > num_values) {
+ TELEMETRY_LOG_ERR("Cannot get metrics values");
+ goto eperm_fail;
+ }
+
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+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_json_format_stat(struct telemetry_impl *telemetry, json_t *stats,
+ const char *metric_name, uint64_t metric_value)
+{
+ int ret;
+ json_t *stat = json_object();
+
+ if (stat == NULL) {
+ TELEMETRY_LOG_ERR("Could not create stat JSON object");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "name", json_string(metric_name));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat Name field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(stat, "value", json_integer(metric_value));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat Value field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(stats, stat);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stat cannot be added to stats json array");
+ goto eperm_fail;
+ }
+
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+}
+
+static int32_t
+rte_telemetry_json_format_port(struct telemetry_impl *telemetry,
+ uint32_t port_id, json_t *ports, uint32_t *metric_ids,
+ uint32_t num_metric_ids)
+{
+ struct rte_metric_value *metrics = 0;
+ struct rte_metric_name *names = 0;
+ int num_metrics, ret, err_ret;
+ json_t *port, *stats;
+ uint32_t i;
+
+ num_metrics = rte_metrics_get_names(NULL, 0);
+ if (num_metrics < 0) {
+ TELEMETRY_LOG_ERR("Cannot get metrics count");
+ goto einval_fail;
+ } else if (num_metrics == 0) {
+ TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
+ goto eperm_fail;
+ }
+
+ metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
+ names = malloc(sizeof(struct rte_metric_name) * num_metrics);
+ if (metrics == NULL || names == NULL) {
+ TELEMETRY_LOG_ERR("Cannot allocate memory");
+ free(metrics);
+ free(names);
+
+ err_ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
+ if (err_ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+ }
+
+ ret = rte_telemetry_get_metrics(telemetry, port_id, metrics, names,
+ num_metrics);
+ if (ret < 0) {
+ free(metrics);
+ free(names);
+ TELEMETRY_LOG_ERR("rte_telemetry_get_metrics failed");
+ return -1;
+ }
+
+ port = json_object();
+ stats = json_array();
+ if (port == NULL || stats == NULL) {
+ TELEMETRY_LOG_ERR("Could not create port/stats JSON objects");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(port, "port", json_integer(port_id));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Port field cannot be set");
+ goto eperm_fail;
+ }
+
+ for (i = 0; i < num_metric_ids; i++) {
+ int metric_id = metric_ids[i];
+ int metric_index = -1;
+ int metric_name_key = -1;
+ int32_t j;
+ uint64_t metric_value;
+
+ if (metric_id >= num_metrics) {
+ TELEMETRY_LOG_ERR("Metric_id: %d is not valid",
+ metric_id);
+ goto einval_fail;
+ }
+
+ for (j = 0; j < num_metrics; j++) {
+ if (metrics[j].key == metric_id) {
+ metric_name_key = metrics[j].key;
+ metric_index = j;
+ break;
+ }
+ }
+
+ const char *metric_name = names[metric_name_key].name;
+ metric_value = metrics[metric_index].value;
+
+ if (metric_name_key < 0 || metric_index < 0) {
+ TELEMETRY_LOG_ERR("Could not get metric name/index");
+ goto eperm_fail;
+ }
+
+ ret = rte_telemetry_json_format_stat(telemetry, stats,
+ metric_name, metric_value);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Format stat with id: %u failed",
+ metric_id);
+ free(metrics);
+ free(names);
+ return -1;
+ }
+ }
+
+ if (json_array_size(stats) == 0)
+ ret = json_object_set_new(port, "stats", json_null());
+ else
+ ret = json_object_set_new(port, "stats", stats);
+
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Stats object cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_array_append_new(ports, port);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Port object cannot be added to ports array");
+ goto eperm_fail;
+ }
+
+ free(metrics);
+ free(names);
+ return 0;
+
+eperm_fail:
+ free(metrics);
+ free(names);
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+einval_fail:
+ free(metrics);
+ free(names);
+ 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_encode_json_format(struct telemetry_impl *telemetry,
+ uint32_t *port_ids, uint32_t num_port_ids, uint32_t *metric_ids,
+ uint32_t num_metric_ids, char **json_buffer)
+{
+ int ret;
+ json_t *root, *ports;
+ uint32_t i;
+
+ if (num_port_ids <= 0 || num_metric_ids <= 0) {
+ TELEMETRY_LOG_ERR("Please provide port and metric ids to query");
+ goto einval_fail;
+ }
+
+ ports = json_array();
+ if (ports == NULL) {
+ TELEMETRY_LOG_ERR("Could not create ports JSON array");
+ goto eperm_fail;
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ if (!rte_eth_dev_is_valid_port(port_ids[i])) {
+ TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]);
+ goto einval_fail;
+ }
+ }
+
+ for (i = 0; i < num_port_ids; i++) {
+ ret = rte_telemetry_json_format_port(telemetry, port_ids[i],
+ ports, metric_ids, num_metric_ids);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Format port in JSON failed");
+ return -1;
+ }
+ }
+
+ root = json_object();
+ if (root == NULL) {
+ TELEMETRY_LOG_ERR("Could not create root JSON object");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "status_code",
+ json_string("Status OK: 200"));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Status code field cannot be set");
+ goto eperm_fail;
+ }
+
+ ret = json_object_set_new(root, "data", ports);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Data field cannot be set");
+ goto eperm_fail;
+ }
+
+ *json_buffer = json_dumps(root, JSON_INDENT(2));
+ json_decref(root);
+ return 0;
+
+eperm_fail:
+ ret = rte_telemetry_send_error_response(telemetry, -EPERM);
+ if (ret < 0)
+ TELEMETRY_LOG_ERR("Could not send error");
+ return -1;
+
+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(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry)
@@ -241,13 +539,20 @@ rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
}

ret = rte_telemetry_update_metrics_ethdev(telemetry,
- port_ids[i], telemetry->reg_index);
+ port_ids[i], telemetry->reg_index);
if (ret < 0) {
TELEMETRY_LOG_ERR("Failed to update ethdev metrics");
return -1;
}
}

+ ret = rte_telemetry_encode_json_format(telemetry, port_ids,
+ num_port_ids, metric_ids, num_metric_ids, &json_buffer);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON encode function failed");
+ return -1;
+ }
+
ret = rte_telemetry_write_to_socket(telemetry, json_buffer);
if (ret < 0) {
TELEMETRY_LOG_ERR("Could not write to socket");
--
2.9.5
Van Haaren, Harry
2018-10-16 00:46:15 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 08/13] telemetry: format json response when sending stats
This patch adds functionality to create a JSON message in
order to send it to a client socket.
When stats are requested by a client, they are retrieved from
the metrics library and encoded in JSON format.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:33 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds all tests for the Telemetry API.
The tests added include a parser test, selftest, and socket
messaging tests.

The parser tests pass valid and invalid messages to the parser
to ensure the correct return values are received.
The selftest tests basic functions in the Telemetry API such as
registering, unregistering, and initialisation.
The socket messaging tests pass messages through the socket and
validates the return message, to ensure the Telemetry API is
responding correctly.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/Makefile | 1 +
lib/librte_telemetry/meson.build | 4 +-
lib/librte_telemetry/rte_telemetry.c | 653 ++++++++++++++++++++++
lib/librte_telemetry/rte_telemetry.h | 12 +
lib/librte_telemetry/rte_telemetry_internal.h | 3 +
lib/librte_telemetry/rte_telemetry_parser_test.c | 534 ++++++++++++++++++
lib/librte_telemetry/rte_telemetry_parser_test.h | 39 ++
lib/librte_telemetry/rte_telemetry_socket_tests.h | 36 ++
lib/librte_telemetry/rte_telemetry_version.map | 1 +
9 files changed, 1281 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c
create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h
create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index 95c7296..1a05069 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -22,6 +22,7 @@ LIBABIVER := 1
# library source files
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c
SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c
+SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser_test.c

# export include files
SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7450f96..57dd83d 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation

-sources = files('rte_telemetry.c', 'rte_telemetry_parser.c')
-headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h')
+sources = files('rte_telemetry.c', 'rte_telemetry_parser.c', 'rte_telemetry_parser_test.c')
+headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h', 'rte_telemetry_parser_test.h')
deps += ['metrics', 'ethdev']
cflags += '-DALLOW_EXPERIMENTAL_API'
jansson = cc.find_library('jansson', required: true)
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 5f59f16..4fdd1d7 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -18,13 +18,32 @@
#include "rte_telemetry.h"
#include "rte_telemetry_internal.h"
#include "rte_telemetry_parser.h"
+#include "rte_telemetry_parser_test.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 {
+ char *test_name;
+ int (*test_func_ptr)(struct telemetry_impl *telemetry, int fd);
+};
+
+struct json_data {
+ char *status_code;
+ char *data;
+ int port;
+ char *stat_name;
+ int stat_value;
+};
+
static void
rte_telemetry_get_runtime_dir(char *socket_path, size_t size)
{
@@ -640,6 +659,7 @@ static int32_t
rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
uint16_t pid;
+ int ret;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
@@ -652,6 +672,18 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

telemetry->metrics_register_done = 1;
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ 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;
}
@@ -1134,6 +1166,627 @@ rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
return -1;
}

+int32_t
+rte_telemetry_dummy_client_socket(const char *valid_client_path)
+{
+ int sockfd = socket(AF_UNIX, SOCK_STREAM, 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 = 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_STREAM, 0);
+ *recv_fd = socket(AF_UNIX, SOCK_STREAM, 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;
+ char *status = "Status Error: Invalid Argument 404";
+ 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";
+ 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;
RTE_INIT(rte_telemetry_register);

diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
index d3b0d8d..958723b 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -33,4 +33,16 @@ rte_telemetry_init(void);
int32_t
rte_telemetry_cleanup(void);

+/**
+ * Runs various tests to ensure telemetry initialisation and register/unregister
+ * functions are working correctly.
+ *
+ * @return
+ * 0 on success when all tests have passed
+ * @return
+ * -1 on failure when the test has failed
+ */
+int32_t
+rte_telemetry_selftest(void);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 0082cb2..de7afda 100644
--- a/lib/librte_telemetry/rte_telemetry_internal.h
+++ b/lib/librte_telemetry/rte_telemetry_internal.h
@@ -75,4 +75,7 @@ int32_t
rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids,
uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry);

+int32_t
+rte_telemetry_socket_messaging_testing(int index, int socket);
+
#endif
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.c b/lib/librte_telemetry/rte_telemetry_parser_test.c
new file mode 100644
index 0000000..9bf66a2
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.c
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <jansson.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_tailq.h>
+#include <rte_string_fns.h>
+
+#include "rte_telemetry_parser.h"
+
+enum choices {
+ INV_ACTION_VAL,
+ INV_COMMAND_VAL,
+ INV_DATA_VAL,
+ INV_ACTION_FIELD,
+ INV_COMMAND_FIELD,
+ INV_DATA_FIELD,
+ INV_JSON_FORMAT,
+ VALID_REQ
+};
+
+
+#define TEST_CLIENT "/var/run/dpdk/test_client"
+
+int32_t
+rte_telemetry_create_test_socket(struct telemetry_impl *telemetry,
+ const char *test_client_path)
+{
+ int ret, sockfd;
+ struct sockaddr_un addr = {0};
+ struct telemetry_client *client;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ TELEMETRY_LOG_ERR("Test socket creation failure");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, test_client_path, sizeof(addr.sun_path));
+ unlink(test_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;
+ }
+
+ ret = rte_telemetry_register_client(telemetry, test_client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Register dummy client failed: %i", ret);
+ return -1;
+ }
+
+ ret = accept(sockfd, NULL, NULL);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Socket accept failed");
+ return -1;
+ }
+
+ TAILQ_FOREACH(client, &telemetry->client_list_head, client_list)
+ telemetry->request_client = client;
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, json_t **data)
+{
+
+ int ret;
+ json_t *stat_names_json_array = NULL;
+ json_t *port_ids_json_array = NULL;
+ uint32_t i;
+
+ if (num_port_ids < 0) {
+ TELEMETRY_LOG_ERR("Port Ids Count invalid");
+ goto fail;
+ }
+
+ *data = json_object();
+ if (*data == NULL) {
+ TELEMETRY_LOG_ERR("Data json object creation failed");
+ goto fail;
+ }
+
+ port_ids_json_array = json_array();
+ if (port_ids_json_array == NULL) {
+ TELEMETRY_LOG_ERR("port_ids_json_array creation failed");
+ goto fail;
+ }
+
+ for (i = 0; i < (uint32_t)num_port_ids; i++) {
+ ret = json_array_append(port_ids_json_array,
+ json_integer(port_ids[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON array creation failed");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "ports", port_ids_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting 'ports' value in data object failed");
+ goto fail;
+ }
+
+ if (stat_names) {
+ if (num_stat_names < 0) {
+ TELEMETRY_LOG_ERR("Stat Names Count invalid");
+ goto fail;
+ }
+
+ stat_names_json_array = json_array();
+ if (stat_names_json_array == NULL) {
+ TELEMETRY_LOG_ERR("stat_names_json_array creation failed");
+ goto fail;
+ }
+
+ uint32_t i;
+ for (i = 0; i < (uint32_t)num_stat_names; i++) {
+ ret = json_array_append(stat_names_json_array,
+ json_string(stat_names[i]));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("JSON array creation failed");
+ goto fail;
+ }
+ }
+
+ ret = json_object_set_new(*data, "stats", stat_names_json_array);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting 'stats' value in data object failed");
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ if (*data)
+ json_decref(*data);
+ if (stat_names_json_array)
+ json_decref(stat_names_json_array);
+ if (port_ids_json_array)
+ json_decref(port_ids_json_array);
+ return -1;
+}
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names, char **request,
+ int inv_choice)
+{
+ int ret;
+ json_t *root = json_object();
+ json_t *data;
+
+ if (root == NULL) {
+ TELEMETRY_LOG_ERR("Could not create root json object");
+ goto fail;
+ }
+
+ if (inv_choice == INV_ACTION_FIELD) {
+ ret = json_object_set_new(root, "ac--on", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid action field in root object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "action", json_integer(action));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid action field in root object failed");
+ goto fail;
+ }
+ }
+
+ if (inv_choice == INV_COMMAND_FIELD) {
+ ret = json_object_set_new(root, "co---nd", json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid command field in root object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "command", json_string(command));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid command field in root object failed");
+ goto fail;
+ }
+ }
+
+ data = json_null();
+ if (client_path) {
+ data = json_object();
+ if (data == NULL) {
+ TELEMETRY_LOG_ERR("Data json object creation failed");
+ goto fail;
+ }
+
+ ret = json_object_set_new(data, "client_path",
+ json_string(client_path));
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid client_path field in data object failed");
+ goto fail;
+ }
+
+ } else if (port_ids) {
+ ret = rte_telemetry_format_port_stat_ids(port_ids, num_port_ids,
+ stat_names, num_stat_names, &data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Formatting Port/Stat arrays failed");
+ goto fail;
+ }
+
+ }
+
+ if (inv_choice == INV_DATA_FIELD) {
+ ret = json_object_set_new(root, "d--a", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting invalid data field in data object failed");
+ goto fail;
+ }
+ } else {
+ ret = json_object_set_new(root, "data", data);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Setting valid data field in data object failed");
+ goto fail;
+ }
+ }
+
+ *request = json_dumps(root, 0);
+ if (*request == NULL) {
+ TELEMETRY_LOG_ERR("Converting JSON root object to char* failed");
+ goto fail;
+ }
+
+ json_decref(root);
+ return 0;
+
+fail:
+ if (root)
+ json_decref(root);
+ return -1;
+}
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice)
+{
+ int ret;
+ char *request;
+ char *client_path_data = NULL;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command_choice = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path_data = "INVALID_DATA";
+
+ ret = rte_telemetry_create_json_request(action_choice, command_choice,
+ client_path_data, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice)
+{
+ int ret;
+ char *request;
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ char *command = "ports_details";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ port_ids = NULL;
+
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const *stat_names, int num_stat_names,
+ int inv_choice)
+{
+ int ret;
+ char *request;
+ char *command = "ports_stats_values_by_name";
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL) {
+ port_ids = NULL;
+ stat_names = NULL;
+ }
+
+ ret = rte_telemetry_create_json_request(action_choice, command, NULL,
+ port_ids, num_port_ids, stat_names, num_stat_names, &request,
+ inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_send_unreg_request(struct telemetry_impl *telemetry,
+ int action_choice, const char *client_path, int inv_choice)
+{
+ int ret;
+ char *request;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ char *command = "clients";
+
+ if (inv_choice == INV_ACTION_VAL)
+ action_choice = -1;
+ else if (inv_choice == INV_COMMAND_VAL)
+ command = "INVALID_COMMAND";
+ else if (inv_choice == INV_DATA_VAL)
+ client_path = NULL;
+
+ ret = rte_telemetry_create_json_request(action_choice, command,
+ client_path, NULL, -1, NULL, -1, &request, inv_choice);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create JSON Request");
+ return -1;
+ }
+
+ if (inv_choice == INV_JSON_FORMAT)
+ request++;
+
+ ret = rte_telemetry_parse(telemetry, request);
+ if (ret < 0) {
+ TELEMETRY_LOG_WARN("Could not parse JSON Request");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry)
+{
+ int ret;
+ const char *client_path = TEST_CLIENT;
+
+ if (telemetry == NULL) {
+ TELEMETRY_LOG_ERR("Telemetry argument has not been initialised");
+ return -EINVAL;
+ }
+
+ ret = rte_telemetry_create_test_socket(telemetry, client_path);
+ if (ret < 0) {
+ TELEMETRY_LOG_ERR("Could not create test request client socket");
+ return -1;
+ }
+
+ int port_ids[] = {0, 1};
+ int num_port_ids = RTE_DIM(port_ids);
+
+ static const char * const stat_names[] = {"tx_good_packets",
+ "rx_good_packets"};
+ int num_stat_names = RTE_DIM(stat_names);
+
+ static const char * const test_types[] = {
+ "INVALID ACTION VALUE TESTS",
+ "INVALID COMMAND VALUE TESTS",
+ "INVALID DATA VALUE TESTS",
+ "INVALID ACTION FIELD TESTS",
+ "INVALID COMMAND FIELD TESTS",
+ "INVALID DATA FIELD TESTS",
+ "INVALID JSON FORMAT TESTS",
+ "VALID TESTS"
+ };
+
+
+#define NUM_TEST_TYPES (sizeof(test_types)/sizeof(const char * const))
+
+ uint32_t i;
+ for (i = 0; i < NUM_TEST_TYPES; i++) {
+ TELEMETRY_LOG_INFO("%s", test_types[i]);
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "ports", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports test passed");
+
+ ret = rte_telemetry_send_get_ports_details_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports details valid");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports details invalid");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports details test passed");
+
+ ret = rte_telemetry_send_get_ports_and_stats_request(telemetry,
+ ACTION_GET, "port_stats", i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get port stats valid test");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats test passed");
+
+ ret = rte_telemetry_send_stats_values_by_name_request(telemetry,
+ ACTION_GET, port_ids, num_port_ids, stat_names,
+ num_stat_names, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats values by name valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Get ports stats values by name invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Get ports stats values by name test passed");
+
+ ret = rte_telemetry_send_unreg_request(telemetry, ACTION_DELETE,
+ client_path, i);
+ if (ret != 0 && i == VALID_REQ) {
+ TELEMETRY_LOG_ERR("Deregister valid test failed");
+ return -EPERM;
+ } else if (ret != -1 && i != VALID_REQ) {
+ TELEMETRY_LOG_ERR("Deregister invalid test failed");
+ return -EPERM;
+ }
+
+ TELEMETRY_LOG_INFO("Success - Deregister test passed");
+ }
+
+ return 0;
+}
diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.h b/lib/librte_telemetry/rte_telemetry_parser_test.h
new file mode 100644
index 0000000..6ada852
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser_test.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#ifndef _RTE_TELEMETRY_PARSER_TEST_H_
+#define _RTE_TELEMETRY_PARSER_TEST_H_
+
+int32_t
+rte_telemetry_parser_test(struct telemetry_impl *telemetry);
+
+int32_t
+rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, json_t **data);
+
+int32_t
+rte_telemetry_create_json_request(int action, char *command,
+ const char *client_path, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names, char **request,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry,
+ int action_choice, char *command_choice, int inv_choice);
+
+int32_t
+rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry,
+ int action_choice, int *port_ids, int num_port_ids, int inv_choice);
+
+int32_t
+rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl
+ *telemetry, int action_choice, int *port_ids, int num_port_ids,
+ const char * const stat_names, int num_stat_names,
+ int inv_choice);
+
+int32_t
+rte_telemetry_send_unreg_request(int action_choice, const char *client_path,
+ int inv_choice);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_socket_tests.h b/lib/librte_telemetry/rte_telemetry_socket_tests.h
new file mode 100644
index 0000000..db9167c
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_socket_tests.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdbool.h>
+
+#include "rte_telemetry_internal.h"
+
+#ifndef _RTE_TELEMETRY_SOCKET_TESTING_H_
+#define _RTE_TELEMETRY_SOCKET_TESTING_H_
+
+int32_t
+rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry,
+ int fd);
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd);
+
+int32_t
+rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
+ int send_fd, int recv_fd);
+
+int32_t
+rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
+ int *recv_fd);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
index 992d227..98459fc 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -2,5 +2,6 @@ DPDK_18.11 {
global:

rte_telemetry_init;
+ rte_telemetry_selftest;
local: *;
};
--
2.9.5
Van Haaren, Harry
2018-10-16 00:46:36 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 09/13] telemetry: add tests for telemetry api
This patch adds all tests for the Telemetry API.
The tests added include a parser test, selftest, and socket
messaging tests.
The parser tests pass valid and invalid messages to the parser
to ensure the correct return values are received.
The selftest tests basic functions in the Telemetry API such as
registering, unregistering, and initialisation.
The socket messaging tests pass messages through the socket and
validates the return message, to ensure the Telemetry API is
responding correctly.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:34 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds functionality to enable/disable the selftest.

This functionality will be extended in future to make the
enabling/disabling more dynamic and remove this 'hardcoded' approach. We
are temporarily using this approach due to the design changes (vdev vs eal)
made to the library.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
lib/librte_telemetry/rte_telemetry.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 4fdd1d7..0e29a76 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -660,6 +660,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
{
uint16_t pid;
int ret;
+ int selftest = 0;

RTE_ETH_FOREACH_DEV(pid) {
telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid);
@@ -672,18 +673,20 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
}

telemetry->metrics_register_done = 1;
- ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
- telemetry->server_fd);
- if (ret < 0)
- return -1;
+ if (selftest) {
+ ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index,
+ 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;
- }
+ 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");
+ TELEMETRY_LOG_INFO("Success - All Parser Tests Passed");
+ }

return 0;
}
--
2.9.5
Van Haaren, Harry
2018-10-16 00:47:00 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 10/13] telemetry: add ability to disable selftest
This patch adds functionality to enable/disable the selftest.
This functionality will be extended in future to make the
enabling/disabling more dynamic and remove this 'hardcoded' approach. We
are temporarily using this approach due to the design changes (vdev vs eal)
made to the library.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:36 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds a python script which can be used as a demo
client. The script is interactive and will allow the user to
register, request statistics, and unregister.

To run the script, an argument for the client file path must
be passed in: "python telemetry_client.py <file_path>".

This script is useful to see how the Telemetry API for DPDK
is used, and how to make the initial connection.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
usertools/dpdk-telemetry-client.py | 116 +++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 usertools/dpdk-telemetry-client.py

diff --git a/usertools/dpdk-telemetry-client.py b/usertools/dpdk-telemetry-client.py
new file mode 100644
index 0000000..71c6be7
--- /dev/null
+++ b/usertools/dpdk-telemetry-client.py
@@ -0,0 +1,116 @@
+# SPDK-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+import socket
+import os
+import sys
+import time
+
+BUFFER_SIZE = 200000
+
+METRICS_REQ = "{\"action\":0,\"command\":\"ports_all_stat_values\",\"data\":null}"
+API_REG = "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\":\""
+API_UNREG = "{\"action\":2,\"command\":\"clients\",\"data\":{\"client_path\":\""
+DEFAULT_FP = "/var/run/dpdk/default_client"
+
+class Socket:
+
+ def __init__(self):
+ self.send_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.client_fd = None
+
+ def __del__(self):
+ try:
+ self.send_fd.close()
+ self.recv_fd.close()
+ self.client_fd.close()
+ except:
+ print("Error - Sockets could not be closed")
+
+class Client:
+
+ def __init__(self): # Creates a client instance
+ self.socket = Socket()
+ self.file_path = None
+ self.choice = None
+ self.unregistered = 0
+
+ def __del__(self):
+ try:
+ if self.unregistered == 0:
+ self.unregister();
+ except:
+ print("Error - Client could not be destroyed")
+
+ def getFilepath(self, file_path): # Gets arguments from Command-Line and assigns to instance of client
+ self.file_path = file_path
+
+ def register(self): # Connects a client to DPDK-instance
+ if os.path.exists(self.file_path):
+ os.unlink(self.file_path)
+ try:
+ self.socket.recv_fd.bind(self.file_path)
+ except socket.error as msg:
+ print ("Error - Socket binding error: " + str(msg) + "\n")
+ self.socket.recv_fd.settimeout(2)
+ self.socket.send_fd.connect("/var/run/dpdk/rte/telemetry")
+ JSON = (API_REG + self.file_path + "\"}}")
+ self.socket.send_fd.sendall(JSON)
+ self.socket.recv_fd.listen(1)
+ self.socket.client_fd = self.socket.recv_fd.accept()[0]
+
+ def unregister(self): # Unregister a given client
+ self.socket.client_fd.send(API_UNREG + self.file_path + "\"}}")
+ self.socket.client_fd.close()
+
+ def requestMetrics(self): # Requests metrics for given client
+ self.socket.client_fd.send(METRICS_REQ)
+ data = self.socket.client_fd.recv(BUFFER_SIZE)
+ print "\nResponse: \n", str(data)
+
+ def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client
+ print("\nPlease enter the number of times you'd like to continuously request Metrics:")
+ n_requests = int(input("\n:"))
+ print("\033[F") #Removes the user input from screen, cleans it up
+ print("\033[K")
+ for i in range(n_requests):
+ self.requestMetrics()
+ time.sleep(sleep_time)
+
+ def interactiveMenu(self, sleep_time): # Creates Interactive menu within the script
+ while self.choice != 3:
+ print("\nOptions Menu")
+ print("[1] Send for Metrics for all ports")
+ print("[2] Send for Metrics for all ports recursively")
+ print("[3] Unregister client")
+
+ try:
+ self.choice = int(input("\n:"))
+ print("\033[F") #Removes the user input for screen, cleans it up
+ print("\033[K")
+ if self.choice == 1:
+ self.requestMetrics()
+ elif self.choice == 2:
+ self.repeatedlyRequestMetrics(sleep_time)
+ elif self.choice == 3:
+ self.unregister()
+ self.unregistered = 1
+ else:
+ print("Error - Invalid request choice")
+ except:
+ pass
+
+if __name__ == "__main__":
+
+ sleep_time = 1
+ file_path = ""
+ if (len(sys.argv) == 2):
+ file_path = sys.argv[1]
+ else:
+ print("Warning - No filepath passed, using default (" + DEFAULT_FP + ").")
+ file_path = DEFAULT_FP
+ client = Client()
+ client.getFilepath(file_path)
+ client.register()
+ client.interactiveMenu(sleep_time)
--
2.9.5
Van Haaren, Harry
2018-10-16 00:47:36 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 12/13] usertools: add client python script for telemetry
This patch adds a python script which can be used as a demo
client. The script is interactive and will allow the user to
register, request statistics, and unregister.
To run the script, an argument for the client file path must
be passed in: "python telemetry_client.py <file_path>".
This script is useful to see how the Telemetry API for DPDK
is used, and how to make the initial connection.
---
Acked-by: Harry van Haaren <***@intel.com>
Kevin Laatz
2018-10-11 16:58:35 UTC
Permalink
From: Ciara Power <***@intel.com>

This patch adds all documentation for telemetry.

A description on how to use the Telemetry API with a DPDK
application is given in this document.

It also adds the MAINTAINERS file entry for telemetry.

Signed-off-by: Ciara Power <***@intel.com>
Signed-off-by: Brian Archbold <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
MAINTAINERS | 5 +++
doc/guides/howto/index.rst | 1 +
doc/guides/howto/telemetry.rst | 85 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 91 insertions(+)
create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 84b9ff7..e695a68 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1168,6 +1168,11 @@ F: test/bpf/
F: test/test/test_bpf.c
F: doc/guides/prog_guide/bpf_lib.rst

+Telemetry
+M: Kevin Laatz <***@intel.com>
+F: lib/librte_telemetry/
+F: usertools/dpdk-telemetry-client.py
+F: doc/guides/howto/telemetry.rst

Test Applications
-----------------
diff --git a/doc/guides/howto/index.rst b/doc/guides/howto/index.rst
index e13a090..a642a2b 100644
--- a/doc/guides/howto/index.rst
+++ b/doc/guides/howto/index.rst
@@ -17,3 +17,4 @@ HowTo Guides
virtio_user_for_container_networking
virtio_user_as_exceptional_path
packet_capture_framework
+ telemetry
diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst
new file mode 100644
index 0000000..3fcb061
--- /dev/null
+++ b/doc/guides/howto/telemetry.rst
@@ -0,0 +1,85 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright(c) 2018 Intel Corporation.
+
+DPDK Telemetry API User Guide
+==============================
+
+This document describes how the Data Plane Development Kit(DPDK) Telemetry API
+is used for querying port statistics from incoming traffic.
+
+Introduction
+------------
+
+The ``librte_telemetry`` provides the functionality so that users may query
+metrics from incoming port traffic. The application which initializes packet
+forwarding will act as the server, sending metrics to the requesting application
+which acts as the client.
+
+In DPDK, applications are used to initialize the ``telemetry``. To view incoming
+traffic on featured ports, the application should be run first (ie. after ports
+are configured). Once the application is running, the service assurance agent
+(for example the collectd plugin) should be run to begin querying the API.
+
+A client connects their Service Assurance application to the DPDK application
+via a UNIX socket. Once a connection is established, a client can send JSON
+messages to the DPDK application requesting metrics via another UNIX client.
+This request is then handled and parsed if valid. The response is then
+formatted in JSON and sent back to the requesting client.
+
+Pre-requisites
+~~~~~~~~~~~~~~
+
+* Python ≥ 2.5
+
+* Jansson library for JSON serialization
+
+Test Environment
+----------------
+
+``telemetry`` offers a range of selftests that a client can run within
+the DPDK application.
+
+Selftests are disabled by default. They can be enabled by setting the 'selftest'
+variable to 1 in rte_telemetry_initial_accept().
+
+Note: this 'hardcoded' value is temporary.
+
+Configuration
+-------------
+
+Enable the telemetry API by modifying the following config option before
+building DPDK::
+
+ CONFIG_RTE_LIBRTE_TELEMETRY=y
+
+Note: Meson will pick this up automatically if ``libjansson`` is available.
+
+Running the Application
+-----------------------
+
+The following steps demonstrate how to run the ``telemetry`` API to query all
+statistics on all active ports, using the ``telemetry_client`` python script
+to query.
+Note: This guide assumes packet generation is applicable and the user is
+testing with testpmd as a DPDK primary application to forward packets, although
+any DPDK application is applicable.
+
+#. Launch testpmd as the primary application with ``telemetry``.::
+
+ ./app/testpmd --telemetry
+
+#. Launch the ``telemetry`` python script with a client filepath.::
+
+ python usertools/telemetry_client.py /var/run/some_client
+
+ The client filepath is going to be used to setup our UNIX connection with the
+ DPDK primary application, in this case ``testpmd``
+ This will initialize a menu where a client can proceed to recursively query
+ statistics, request statistics once or unregister the file_path, thus exiting
+ the menu.
+
+#. Send traffic to any or all available ports from a traffic generator.
+ Select a query option(recursive or singular polling).
+ The metrics will then be displayed on the client terminal in JSON format.
+
+#. Once finished, unregister the client using the menu command.
--
2.9.5
Van Haaren, Harry
2018-10-16 00:47:21 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 11/13] doc: add telemetry documentation
This patch adds all documentation for telemetry.
A description on how to use the Telemetry API with a DPDK
application is given in this document.
It also adds the MAINTAINERS file entry for telemetry.
---
Acked-by: Harry van Haaren <harry
Kevin Laatz
2018-10-11 16:58:37 UTC
Permalink
This patch adds telemetry as a dependecy to all applications. Without these
changes, the --telemetry flag will not be recognised and applications will
fail to run if they want to enable telemetry.

Signed-off-by: Bruce Richardson <***@intel.com>
Signed-off-by: Kevin Laatz <***@intel.com>
---
app/meson.build | 4 ++--
app/pdump/meson.build | 2 +-
app/proc-info/meson.build | 2 +-
app/test-bbdev/meson.build | 2 +-
app/test-crypto-perf/meson.build | 2 +-
app/test-pmd/meson.build | 2 +-
config/meson.build | 3 +++
lib/meson.build | 1 +
meson.build | 1 +
9 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/app/meson.build b/app/meson.build
index 99e0b93..8e716bf 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -24,7 +24,7 @@ foreach app:apps
# use "deps" for internal DPDK dependencies, and "ext_deps" for
# external package/library requirements
ext_deps = []
- deps = []
+ deps = ['telemetry']

subdir(name)

@@ -38,7 +38,7 @@ foreach app:apps

link_libs = []
if get_option('default_library') == 'static'
- link_libs = dpdk_drivers
+ link_libs = dpdk_static_libraries + dpdk_drivers
endif

if allow_experimental_apis
diff --git a/app/pdump/meson.build b/app/pdump/meson.build
index 988cb4e..116c27f 100644
--- a/app/pdump/meson.build
+++ b/app/pdump/meson.build
@@ -3,4 +3,4 @@

sources = files('main.c')
allow_experimental_apis = true
-deps = ['ethdev', 'kvargs', 'pdump']
+deps += ['ethdev', 'kvargs', 'pdump']
diff --git a/app/proc-info/meson.build b/app/proc-info/meson.build
index 9c148e3..a52b2ee 100644
--- a/app/proc-info/meson.build
+++ b/app/proc-info/meson.build
@@ -3,4 +3,4 @@

sources = files('main.c')
allow_experimental_apis = true
-deps = ['ethdev', 'metrics']
+deps += ['ethdev', 'metrics']
diff --git a/app/test-bbdev/meson.build b/app/test-bbdev/meson.build
index 653907d..eb8cc04 100644
--- a/app/test-bbdev/meson.build
+++ b/app/test-bbdev/meson.build
@@ -6,4 +6,4 @@ sources = files('main.c',
'test_bbdev_perf.c',
'test_bbdev_vector.c')
allow_experimental_apis = true
-deps = ['bbdev', 'bus_vdev']
+deps += ['bbdev', 'bus_vdev']
diff --git a/app/test-crypto-perf/meson.build b/app/test-crypto-perf/meson.build
index eacd7a0..d735b18 100644
--- a/app/test-crypto-perf/meson.build
+++ b/app/test-crypto-perf/meson.build
@@ -12,4 +12,4 @@ sources = files('cperf_ops.c',
'cperf_test_vectors.c',
'cperf_test_verify.c',
'main.c')
-deps = ['cryptodev']
+deps += ['cryptodev']
diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build
index a0b3be0..c24f75f 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -22,7 +22,7 @@ sources = files('cmdline.c',
'testpmd.c',
'txonly.c')

-deps = ['ethdev', 'gro', 'gso', 'cmdline', 'metrics', 'meter', 'bus_pci']
+deps += ['ethdev', 'gro', 'gso', 'cmdline', 'metrics', 'meter', 'bus_pci']
if dpdk_conf.has('RTE_LIBRTE_PDUMP')
deps += 'pdump'
endif
diff --git a/config/meson.build b/config/meson.build
index 6f9228c..275f00b 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -21,6 +21,9 @@ toolchain = cc.get_id()
dpdk_conf.set_quoted('RTE_TOOLCHAIN', toolchain)
dpdk_conf.set('RTE_TOOLCHAIN_' + toolchain.to_upper(), 1)

+add_project_link_arguments('-Wl,--no-as-needed', language: 'c')
+dpdk_extra_ldflags += '-Wl,--no-as-needed'
+
# use pthreads
add_project_link_arguments('-pthread', language: 'c')
dpdk_extra_ldflags += '-pthread'
diff --git a/lib/meson.build b/lib/meson.build
index b5612ad..ef3f3a6 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -127,6 +127,7 @@ foreach l:libraries
dependencies: shared_deps)

dpdk_libraries = [shared_lib] + dpdk_libraries
+ dpdk_static_libraries = [static_lib] + dpdk_static_libraries
endif # sources.length() > 0

set_variable('shared_' + libname, shared_dep)
diff --git a/meson.build b/meson.build
index c9af335..f3bddb2 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,7 @@ project('DPDK', 'C',
cc = meson.get_compiler('c')
dpdk_conf = configuration_data()
dpdk_libraries = []
+dpdk_static_libraries = []
dpdk_drivers = []
dpdk_extra_ldflags = []
--
2.9.5
Van Haaren, Harry
2018-10-16 00:47:55 UTC
Permalink
-----Original Message-----
From: Laatz, Kevin
Sent: Thursday, October 11, 2018 9:59 AM
Subject: [PATCH v4 13/13] build: add dependency on telemetry to apps in meson
This patch adds telemetry as a dependecy to all applications. Without these
changes, the --telemetry flag will not be recognised and applications will
fail to run if they want to enable telemetry.
---
Acked-by: Harry van Haaren <***@intel.com>

Loading...