All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] introduce telemetry library
@ 2018-08-23 12:08 Ciara Power
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
                   ` (11 more replies)
  0 siblings, 12 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 23:17   ` Stephen Hemminger
                     ` (6 more replies)
  2018-08-23 12:08 ` [PATCH 02/11] telemetry: add initial connection socket Ciara Power
                   ` (10 subsequent siblings)
  11 siblings, 7 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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 = 'RTE_LIBRTE_@0@_PMD'
+driver_name_fmt = 'rte_pmd_@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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 02/11] telemetry: add initial connection socket
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-28 16:40   ` Gaëtan Rivet
  2018-09-07  9:48   ` Burakov, Anatoly
  2018-08-23 12:08 ` [PATCH 03/11] telemetry: add client feature and sockets Ciara Power
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 03/11] telemetry: add client feature and sockets
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
  2018-08-23 12:08 ` [PATCH 02/11] telemetry: add initial connection socket Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 23:27   ` Stephen Hemminger
  2018-08-23 12:08 ` [PATCH 04/11] telemetry: add parser for client socket messages Ciara Power
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 04/11] telemetry: add parser for client socket messages
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (2 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 03/11] telemetry: add client feature and sockets Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-30 23:57   ` Gaëtan Rivet
  2018-08-23 12:08 ` [PATCH 05/11] telemetry: update metrics before sending stats Ciara Power
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 05/11] telemetry: update metrics before sending stats
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (3 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 04/11] telemetry: add parser for client socket messages Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 12:08 ` [PATCH 06/11] telemetry: format json response when " Ciara Power
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 06/11] telemetry: format json response when sending stats
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (4 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 05/11] telemetry: update metrics before sending stats Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 12:08 ` [PATCH 07/11] telemetry: add tests for telemetry api Ciara Power
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 07/11] telemetry: add tests for telemetry api
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (5 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 06/11] telemetry: format json response when " Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 23:15   ` Stephen Hemminger
  2018-08-23 12:08 ` [PATCH 08/11] telemetry: add vdev kvargs for selftest Ciara Power
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 08/11] telemetry: add vdev kvargs for selftest
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (6 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 07/11] telemetry: add tests for telemetry api Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 12:08 ` [PATCH 09/11] doc: add telemetry documentation Ciara Power
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 09/11] doc: add telemetry documentation
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (7 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 08/11] telemetry: add vdev kvargs for selftest Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-09-25  8:53   ` Kovacevic, Marko
  2018-08-23 12:08 ` [PATCH 10/11] usertools: add client python script for telemetry Ciara Power
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 10/11] usertools: add client python script for telemetry
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (8 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 09/11] doc: add telemetry documentation Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-08-23 12:08 ` [PATCH 11/11] telemetry: add collectd plugin patch Ciara Power
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
  11 siblings, 0 replies; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH 11/11] telemetry: add collectd plugin patch
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (9 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 10/11] usertools: add client python script for telemetry Ciara Power
@ 2018-08-23 12:08 ` Ciara Power
  2018-09-18  9:52   ` Thomas Monjalon
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
  11 siblings, 1 reply; 220+ messages in thread
From: Ciara Power @ 2018-08-23 12:08 UTC (permalink / raw)
  To: harry.van.haaren, brian.archbold, emma.kenny, ciara.power; +Cc: dev

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 <brian.archbold@intel.com>
Signed-off-by: Emma Kenny <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 <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 <emma.kenny@intel.com>
+Signed-off-by: Brian Archbold <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 @@
+ @LOAD_PLUGIN_LOGFILE@LoadPlugin logfile
+ @LOAD_PLUGIN_LOG_LOGSTASH@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 @@
+ #@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns
+ #@BUILD_PLUGIN_DPDKEVENTS_TRUE@LoadPlugin dpdkevents
+ #@BUILD_PLUGIN_DPDKSTAT_TRUE@LoadPlugin dpdkstat
++@BUILD_PLUGIN_DPDK_TELEMETRY_TRUE@LoadPlugin dpdk_telemetry
+ #@BUILD_PLUGIN_DRBD_TRUE@LoadPlugin drbd
+ #@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email
+ #@BUILD_PLUGIN_ENTROPY_TRUE@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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH 07/11] telemetry: add tests for telemetry api
  2018-08-23 12:08 ` [PATCH 07/11] telemetry: add tests for telemetry api Ciara Power
@ 2018-08-23 23:15   ` Stephen Hemminger
  0 siblings, 0 replies; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:15 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:09 +0100
Ciara Power <ciara.power@intel.com> wrote:

> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> ---
>  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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
@ 2018-08-23 23:17   ` Stephen Hemminger
  2018-08-23 23:17   ` Stephen Hemminger
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:17 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:03 +0100
Ciara Power <ciara.power@intel.com> wrote:

> +
> +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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
  2018-08-23 23:17   ` Stephen Hemminger
@ 2018-08-23 23:17   ` Stephen Hemminger
  2018-08-23 23:18   ` Stephen Hemminger
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:17 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:03 +0100
Ciara Power <ciara.power@intel.com> wrote:

> +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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
  2018-08-23 23:17   ` Stephen Hemminger
  2018-08-23 23:17   ` Stephen Hemminger
@ 2018-08-23 23:18   ` Stephen Hemminger
  2018-08-23 23:19   ` Stephen Hemminger
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:18 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:03 +0100
Ciara Power <ciara.power@intel.com> wrote:

> +			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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
                     ` (2 preceding siblings ...)
  2018-08-23 23:18   ` Stephen Hemminger
@ 2018-08-23 23:19   ` Stephen Hemminger
  2018-08-23 23:22   ` Stephen Hemminger
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:19 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:03 +0100
Ciara Power <ciara.power@intel.com> wrote:

> +/* 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);
> +

Can this be done with RTE_INIT (i.e automatic constructor).

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
                     ` (3 preceding siblings ...)
  2018-08-23 23:19   ` Stephen Hemminger
@ 2018-08-23 23:22   ` Stephen Hemminger
  2018-08-28 17:12     ` Van Haaren, Harry
  2018-08-24 13:03   ` Shreyansh Jain
  2018-08-28 11:46   ` Gaëtan Rivet
  6 siblings, 1 reply; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:22 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:03 +0100
Ciara Power <ciara.power@intel.com> wrote:

> +/* 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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 03/11] telemetry: add client feature and sockets
  2018-08-23 12:08 ` [PATCH 03/11] telemetry: add client feature and sockets Ciara Power
@ 2018-08-23 23:27   ` Stephen Hemminger
  2018-08-28 15:26     ` Hunt, David
  0 siblings, 1 reply; 220+ messages in thread
From: Stephen Hemminger @ 2018-08-23 23:27 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

On Thu, 23 Aug 2018 13:08:05 +0100
Ciara Power <ciara.power@intel.com> wrote:

> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>

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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
                     ` (4 preceding siblings ...)
  2018-08-23 23:22   ` Stephen Hemminger
@ 2018-08-24 13:03   ` Shreyansh Jain
  2018-08-28 16:50     ` Van Haaren, Harry
  2018-08-28 11:46   ` Gaëtan Rivet
  6 siblings, 1 reply; 220+ messages in thread
From: Shreyansh Jain @ 2018-08-24 13:03 UTC (permalink / raw)
  To: Ciara Power, harry.van.haaren, brian.archbold, emma.kenny; +Cc: dev

On Thursday 23 August 2018 05:38 PM, Ciara Power wrote:
> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> ---

[...]

/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 {

         ^^^^^
I think you want 18.11 here.

> +	global:
> +
> +	telemetry_probe;
> +	telemetry_remove;
> +
> +	local: *;
> +
> +};


[...]

> 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?

> +		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.

> +		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?

> +	}
> +	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.

> +}
> +
> +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);
> +}

[...]

> +#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.

> +	global:
> +
> +	rte_telemetry_init;
> +	local: *;
> +};

[...]

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
                     ` (5 preceding siblings ...)
  2018-08-24 13:03   ` Shreyansh Jain
@ 2018-08-28 11:46   ` Gaëtan Rivet
  2018-08-28 16:54     ` Van Haaren, Harry
  6 siblings, 1 reply; 220+ messages in thread
From: Gaëtan Rivet @ 2018-08-28 11:46 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

Hi Ciara,

On Thu, Aug 23, 2018 at 01:08:03PM +0100, Ciara Power wrote:
> 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.

> Control threads are used to get CPU cycles for telemetry, which
> are configured in this patch also.
> 
> Signed-off-by: Ciara Power <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>

Regards,
-- 
Gaëtan Rivet
6WIND

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 03/11] telemetry: add client feature and sockets
  2018-08-23 23:27   ` Stephen Hemminger
@ 2018-08-28 15:26     ` Hunt, David
  2018-08-28 17:09       ` Van Haaren, Harry
  0 siblings, 1 reply; 220+ messages in thread
From: Hunt, David @ 2018-08-28 15:26 UTC (permalink / raw)
  To: Stephen Hemminger, Ciara Power
  Cc: harry.van.haaren, brian.archbold, emma.kenny, dev


On 24/8/2018 12:27 AM, Stephen Hemminger wrote:
> On Thu, 23 Aug 2018 13:08:05 +0100
> Ciara Power <ciara.power@intel.com> wrote:
>
>> 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 <ciara.power@intel.com>
>> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> 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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 02/11] telemetry: add initial connection socket
  2018-08-23 12:08 ` [PATCH 02/11] telemetry: add initial connection socket Ciara Power
@ 2018-08-28 16:40   ` Gaëtan Rivet
  2018-08-28 17:03     ` Van Haaren, Harry
  2018-09-07  9:48   ` Burakov, Anatoly
  1 sibling, 1 reply; 220+ messages in thread
From: Gaëtan Rivet @ 2018-08-28 16:40 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

Hi Ciara,

On Thu, Aug 23, 2018 at 01:08:04PM +0100, Ciara Power wrote:
> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <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;
> +}
> +

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?

> +static int32_t
> +rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)

"_to" might not be necessary.

> +{
> +	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.

> +		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.

> +		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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-24 13:03   ` Shreyansh Jain
@ 2018-08-28 16:50     ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-08-28 16:50 UTC (permalink / raw)
  To: Shreyansh Jain, Power, Ciara, Archbold, Brian, Kenny, Emma; +Cc: dev

> From: Shreyansh Jain [mailto:shreyansh.jain@nxp.com]
> Sent: Friday, August 24, 2018 2:04 PM
> To: Power, Ciara <ciara.power@intel.com>; Van Haaren, Harry
> <harry.van.haaren@intel.com>; Archbold, Brian <brian.archbold@intel.com>;
> Kenny, Emma <emma.kenny@intel.com>
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
> infrastructure
> 
> On Thursday 23 August 2018 05:38 PM, Ciara Power wrote:
> > 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 <ciara.power@intel.com>
> > Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> > ---
> 
> [...]
> 
> /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 {
> 
>          ^^^^^
> I think you want 18.11 here.

Yes indeed.


> > +	global:
> > +
> > +	telemetry_probe;
> > +	telemetry_remove;
> > +
> > +	local: *;
> > +
> > +};
> 
> 
> [...]
> 
> > 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?

Yes, as Stephen noted too. Will fix!

> 
> > +		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.
> 
> > +		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.


> > +	}
> > +	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.

> > +}
> > +
> > +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);
> > +}
> 
> [...]
> 
> > +#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.

Correct.

Thanks for review, hopefully see you at Userspace where
there will be a lightning talk about this patchset.

Cheers, -Harry

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-28 11:46   ` Gaëtan Rivet
@ 2018-08-28 16:54     ` Van Haaren, Harry
  2018-08-29  8:23       ` Gaëtan Rivet
  0 siblings, 1 reply; 220+ messages in thread
From: Van Haaren, Harry @ 2018-08-28 16:54 UTC (permalink / raw)
  To: Gaëtan Rivet, Power, Ciara; +Cc: Archbold, Brian, Kenny, Emma, dev

Hi Gaetan,

> -----Original Message-----
> From: Gaëtan Rivet [mailto:gaetan.rivet@6wind.com]
> Sent: Tuesday, August 28, 2018 12:47 PM
> To: Power, Ciara <ciara.power@intel.com>
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>; Archbold, Brian
> <brian.archbold@intel.com>; Kenny, Emma <emma.kenny@intel.com>; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
> infrastructure
> 
> Hi Ciara,
> 
> On Thu, Aug 23, 2018 at 01:08:03PM +0100, Ciara Power wrote:
> > 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?


> > Control threads are used to get CPU cycles for telemetry, which
> > are configured in this patch also.
> >
> > Signed-off-by: Ciara Power <ciara.power@intel.com>
> > Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> 
> Regards,
> --
> Gaëtan Rivet
> 6WIND

Thanks for review, and there's a lightning talk at Userspace so please
do provide input there too :) -Harry

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 02/11] telemetry: add initial connection socket
  2018-08-28 16:40   ` Gaëtan Rivet
@ 2018-08-28 17:03     ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-08-28 17:03 UTC (permalink / raw)
  To: Gaëtan Rivet, Power, Ciara; +Cc: Archbold, Brian, Kenny, Emma, dev

> From: Gaëtan Rivet [mailto:gaetan.rivet@6wind.com]
> Sent: Tuesday, August 28, 2018 5:40 PM
> To: Power, Ciara <ciara.power@intel.com>
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>; Archbold, Brian
> <brian.archbold@intel.com>; Kenny, Emma <emma.kenny@intel.com>; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH 02/11] telemetry: add initial connection
> socket

<snip>

> > +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.


> > +static int32_t
> > +rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id)
> 
> "_to" might not be necessary.
> 
> > +{
> > +	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.


> > +		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.


> > +		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!

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 03/11] telemetry: add client feature and sockets
  2018-08-28 15:26     ` Hunt, David
@ 2018-08-28 17:09       ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-08-28 17:09 UTC (permalink / raw)
  To: Hunt, David, Stephen Hemminger, Power, Ciara
  Cc: Archbold, Brian, Kenny, Emma, dev

> -----Original Message-----
> From: Hunt, David
> Sent: Tuesday, August 28, 2018 4:27 PM
> To: Stephen Hemminger <stephen@networkplumber.org>; Power, Ciara
> <ciara.power@intel.com>
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>; Archbold, Brian
> <brian.archbold@intel.com>; Kenny, Emma <emma.kenny@intel.com>; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH 03/11] telemetry: add client feature and
> sockets
> 
> 
> On 24/8/2018 12:27 AM, Stephen Hemminger wrote:
> > On Thu, 23 Aug 2018 13:08:05 +0100
> > Ciara Power <ciara.power@intel.com> wrote:
> >
> >> 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 <ciara.power@intel.com>
> >> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> > 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.



^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-23 23:22   ` Stephen Hemminger
@ 2018-08-28 17:12     ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-08-28 17:12 UTC (permalink / raw)
  To: Stephen Hemminger, Power, Ciara; +Cc: Archbold, Brian, Kenny, Emma, dev

> From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> Sent: Friday, August 24, 2018 12:22 AM
> To: Power, Ciara <ciara.power@intel.com>
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>; Archbold, Brian
> <brian.archbold@intel.com>; Kenny, Emma <emma.kenny@intel.com>; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
> infrastructure
> 
> On Thu, 23 Aug 2018 13:08:03 +0100
> Ciara Power <ciara.power@intel.com> wrote:
> 
> > +/* 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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 01/11] telemetry: initial telemetry infrastructure
  2018-08-28 16:54     ` Van Haaren, Harry
@ 2018-08-29  8:23       ` Gaëtan Rivet
  0 siblings, 0 replies; 220+ messages in thread
From: Gaëtan Rivet @ 2018-08-29  8:23 UTC (permalink / raw)
  To: Van Haaren, Harry; +Cc: Power, Ciara, Archbold, Brian, Kenny, Emma, dev

On Tue, Aug 28, 2018 at 04:54:33PM +0000, Van Haaren, Harry wrote:
> Hi Gaetan,
> 
> > -----Original Message-----
> > From: Gaëtan Rivet [mailto:gaetan.rivet@6wind.com]
> > Sent: Tuesday, August 28, 2018 12:47 PM
> > To: Power, Ciara <ciara.power@intel.com>
> > Cc: Van Haaren, Harry <harry.van.haaren@intel.com>; Archbold, Brian
> > <brian.archbold@intel.com>; Kenny, Emma <emma.kenny@intel.com>; dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH 01/11] telemetry: initial telemetry
> > infrastructure
> > 
> > Hi Ciara,
> > 
> > On Thu, Aug 23, 2018 at 01:08:03PM +0100, Ciara Power wrote:
> > > 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.

> 
> > 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.

> > > Control threads are used to get CPU cycles for telemetry, which
> > > are configured in this patch also.
> > >
> > > Signed-off-by: Ciara Power <ciara.power@intel.com>
> > > Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> > 
> > 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 04/11] telemetry: add parser for client socket messages
  2018-08-23 12:08 ` [PATCH 04/11] telemetry: add parser for client socket messages Ciara Power
@ 2018-08-30 23:57   ` Gaëtan Rivet
  0 siblings, 0 replies; 220+ messages in thread
From: Gaëtan Rivet @ 2018-08-30 23:57 UTC (permalink / raw)
  To: Ciara Power; +Cc: harry.van.haaren, brian.archbold, emma.kenny, dev

Hi,

On Thu, Aug 23, 2018 at 01:08:06PM +0100, Ciara Power wrote:
> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <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);

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.

> +				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.

> +
> +struct command {

struct rte_telemetry_command might be a better name.

> +	char *command_text;

command.command_text? Why not simply text?

> +	int (*comm_func_ptr)(struct telemetry_impl *, int, json_t *);

Function pointers should be typedef for readability.

> +} 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.

> +	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

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

> +
> +	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;

Previous functions used gotos for dealing with errors, this one repeats
the pattern. Seems like an oversight.

> +	}
> +
> +	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.

> +
> +	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]

> +
> +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.

> +		free(names);
> +		goto einval_fail;
> +	}
> +

You are not freeing "names" here, nor are you returning it.

> +	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;

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.

> +
> +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}

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,
                },
        },
        ...
};

> +	};
> +
> +	const uint32_t num_commands = sizeof(commands)/sizeof(struct command);

RTE_DIM() should be used for this.

> +	uint32_t i;
> +

Here action should be checked against the enum previously described,
verifying that it is within range.

> +	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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 02/11] telemetry: add initial connection socket
  2018-08-23 12:08 ` [PATCH 02/11] telemetry: add initial connection socket Ciara Power
  2018-08-28 16:40   ` Gaëtan Rivet
@ 2018-09-07  9:48   ` Burakov, Anatoly
  1 sibling, 0 replies; 220+ messages in thread
From: Burakov, Anatoly @ 2018-09-07  9:48 UTC (permalink / raw)
  To: Ciara Power, harry.van.haaren, brian.archbold, emma.kenny; +Cc: dev

On 23-Aug-18 1:08 PM, Ciara Power wrote:
> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> ---

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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 11/11] telemetry: add collectd plugin patch
  2018-08-23 12:08 ` [PATCH 11/11] telemetry: add collectd plugin patch Ciara Power
@ 2018-09-18  9:52   ` Thomas Monjalon
  2018-09-19 11:09     ` Laatz, Kevin
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-09-18  9:52 UTC (permalink / raw)
  To: Ciara Power; +Cc: dev, harry.van.haaren, brian.archbold, emma.kenny

23/08/2018 14:08, 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?

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 11/11] telemetry: add collectd plugin patch
  2018-09-18  9:52   ` Thomas Monjalon
@ 2018-09-19 11:09     ` Laatz, Kevin
  0 siblings, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-09-19 11:09 UTC (permalink / raw)
  To: Thomas Monjalon, Ciara Power
  Cc: dev, harry.van.haaren, brian.archbold, emma.kenny

On 18/09/2018 10:52, Thomas Monjalon wrote:
> 23/08/2018 14:08, 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH 09/11] doc: add telemetry documentation
  2018-08-23 12:08 ` [PATCH 09/11] doc: add telemetry documentation Ciara Power
@ 2018-09-25  8:53   ` Kovacevic, Marko
  0 siblings, 0 replies; 220+ messages in thread
From: Kovacevic, Marko @ 2018-09-25  8:53 UTC (permalink / raw)
  To: Ciara Power, Van Haaren, Harry, Archbold, Brian, Kenny, Emma,
	Power, Ciara
  Cc: dev

 
> 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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> ---
>  doc/guides/howto/index.rst     |  1 +
>  doc/guides/howto/telemetry.rst | 86
> ++++++++++++++++++++++++++++++++++++++++++

Acked-by: Marko Kovacevic <marko.kovacevic@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v2 00/10] introduce telemetry library
  2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
                   ` (10 preceding siblings ...)
  2018-08-23 12:08 ` [PATCH 11/11] telemetry: add collectd plugin patch Ciara Power
@ 2018-10-03 17:36 ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 01/10] telemetry: initial telemetry infrastructure Kevin Laatz
                     ` (12 more replies)
  11 siblings, 13 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Kevin Laatz

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: 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.

---
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

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

 MAINTAINERS                                       |    5 +
 config/common_base                                |    5 +
 doc/guides/howto/index.rst                        |    1 +
 doc/guides/howto/telemetry.rst                    |   85 +
 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                     |   30 +
 lib/librte_telemetry/meson.build                  |    9 +
 lib/librte_telemetry/rte_telemetry.c              | 1786 +++++++++++++++++++++
 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                                   |    2 +-
 mk/rte.app.mk                                     |    1 +
 usertools/dpdk-telemetry-client.py                |  116 ++
 22 files changed, 3447 insertions(+), 2 deletions(-)
 create mode 100644 doc/guides/howto/telemetry.rst
 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v2 01/10] telemetry: initial telemetry infrastructure
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-04 14:13     ` Gaëtan Rivet
  2018-10-03 17:36   ` [PATCH v2 02/10] telemetry: add initial connection socket Kevin Laatz
                     ` (11 subsequent siblings)
  12 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 02/10] telemetry: add initial connection socket
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 01/10] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 18:40     ` Mattias Rönnblom
  2018-10-03 17:36   ` [PATCH v2 03/10] telemetry: add client feature and sockets Kevin Laatz
                     ` (10 subsequent siblings)
  12 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 03/10] telemetry: add client feature and sockets
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 01/10] telemetry: initial telemetry infrastructure Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 02/10] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 19:06     ` Mattias Rönnblom
  2018-10-03 17:36   ` [PATCH v2 04/10] telemetry: add parser for client socket messages Kevin Laatz
                     ` (9 subsequent siblings)
  12 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 04/10] telemetry: add parser for client socket messages
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (2 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 03/10] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 05/10] telemetry: update metrics before sending stats Kevin Laatz
                     ` (8 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 05/10] telemetry: update metrics before sending stats
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (3 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 04/10] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 06/10] telemetry: format json response when " Kevin Laatz
                     ` (7 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 06/10] telemetry: format json response when sending stats
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (4 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 05/10] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 07/10] telemetry: add tests for telemetry api Kevin Laatz
                     ` (6 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 07/10] telemetry: add tests for telemetry api
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (5 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 06/10] telemetry: format json response when " Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 08/10] telemetry: add ability to disable selftest Kevin Laatz
                     ` (5 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 08/10] telemetry: add ability to disable selftest
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (6 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 07/10] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 09/10] doc: add telemetry documentation Kevin Laatz
                     ` (4 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <ciara.power@intel.com>

This patch adds functionality to enable/disable the selftest.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 09/10] doc: add telemetry documentation
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (7 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 08/10] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-03 17:36   ` [PATCH v2 10/10] usertools: add client python script for telemetry Kevin Laatz
                     ` (3 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v2 10/10] usertools: add client python script for telemetry
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (8 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 09/10] doc: add telemetry documentation Kevin Laatz
@ 2018-10-03 17:36   ` Kevin Laatz
  2018-10-04 13:00   ` [PATCH v2 00/10] introduce telemetry library Van Haaren, Harry
                     ` (2 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-03 17:36 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 02/10] telemetry: add initial connection socket
  2018-10-03 17:36   ` [PATCH v2 02/10] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-03 18:40     ` Mattias Rönnblom
  2018-10-03 19:36       ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-03 18:40 UTC (permalink / raw)
  To: Kevin Laatz, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold

On 2018-10-03 19:36, Kevin Laatz wrote:
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <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"
> +

FHS 3.0 recommends "/run" over "/var/run". Maybe a more descriptive 
macro name would be in order, too.

> +const char *socket_path = DEFAULT_DPDK_PATH;

Is there really a need for this variable? Why not use the macro directly?

>   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.

> +		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.

> +
> +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;

Ethernet port ids are uint16_t.

> +
> +	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).

> +			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.

> @@ -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.

> +
> +	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.

> +
> +	if (!telemetry)

1.8.1

> +		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?

> +		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.

> +		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.

>   	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
> 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 03/10] telemetry: add client feature and sockets
  2018-10-03 17:36   ` [PATCH v2 03/10] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-03 19:06     ` Mattias Rönnblom
  0 siblings, 0 replies; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-03 19:06 UTC (permalink / raw)
  To: Kevin Laatz, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold

On 2018-10-03 19:36, Kevin Laatz wrote:
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <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;
> +	}

1.8.1 here again, and in many instances below.

> +
> +	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.

> +	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()?

> +		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?

> +		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.

> +
> +	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).

> +	buf[buffer_read] = '\0';

If buffer_read == -1, the above isn't a good idea.

> +
> +	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?

> +	}
> +
> +	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.

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

Setting all the fields, so no need to clear the struct.

> +
> +	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.

> +	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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 02/10] telemetry: add initial connection socket
  2018-10-03 18:40     ` Mattias Rönnblom
@ 2018-10-03 19:36       ` Thomas Monjalon
  2018-10-03 19:49         ` Mattias Rönnblom
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-03 19:36 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: Mattias Rönnblom, dev, harry.van.haaren, stephen,
	gaetan.rivet, shreyansh.jain, Ciara Power, Brian Archbold

03/10/2018 20:40, Mattias Rönnblom:
> On 2018-10-03 19:36, Kevin Laatz wrote:
> > +#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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 02/10] telemetry: add initial connection socket
  2018-10-03 19:36       ` Thomas Monjalon
@ 2018-10-03 19:49         ` Mattias Rönnblom
  0 siblings, 0 replies; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-03 19:49 UTC (permalink / raw)
  To: Thomas Monjalon, Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	Ciara Power, Brian Archbold

On 2018-10-03 21:36, Thomas Monjalon wrote:
> 03/10/2018 20:40, Mattias Rönnblom:
>> On 2018-10-03 19:36, Kevin Laatz wrote:
>>> +#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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (9 preceding siblings ...)
  2018-10-03 17:36   ` [PATCH v2 10/10] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-04 13:00   ` Van Haaren, Harry
  2018-10-04 13:25   ` Van Haaren, Harry
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
  12 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-04 13:00 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, Richardson, Bruce

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Wednesday, October 3, 2018 6:36 PM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; Laatz, Kevin <kevin.laatz@intel.com>
> 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.
> 
> ---
> 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


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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (10 preceding siblings ...)
  2018-10-04 13:00   ` [PATCH v2 00/10] introduce telemetry library Van Haaren, Harry
@ 2018-10-04 13:25   ` Van Haaren, Harry
  2018-10-04 15:16     ` Gaëtan Rivet
  2018-10-04 15:53     ` Thomas Monjalon
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
  12 siblings, 2 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-04 13:25 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, Richardson, Bruce

> -----Original Message-----
> From: Van Haaren, Harry
> Sent: Thursday, October 4, 2018 2:00 PM
> To: Laatz, Kevin <kevin.laatz@intel.com>; dev@dpdk.org
> Cc: stephen@networkplumber.org; gaetan.rivet@6wind.com;
> shreyansh.jain@nxp.com; thomas@monjalon.net; Richardson, Bruce
> <bruce.richardson@intel.com>
> Subject: RE: [PATCH v2 00/10] introduce telemetry library
> 
> > -----Original Message-----
> > From: Laatz, Kevin
> > Sent: Wednesday, October 3, 2018 6:36 PM
> > To: dev@dpdk.org
> > Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> > stephen@networkplumber.org; gaetan.rivet@6wind.com;
> shreyansh.jain@nxp.com;
> > thomas@monjalon.net; Laatz, Kevin <kevin.laatz@intel.com>
> > 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.
> >
> > ---
> > 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
> 
> 
> 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 01/10] telemetry: initial telemetry infrastructure
  2018-10-03 17:36   ` [PATCH v2 01/10] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-04 14:13     ` Gaëtan Rivet
  0 siblings, 0 replies; 220+ messages in thread
From: Gaëtan Rivet @ 2018-10-04 14:13 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, shreyansh.jain, thomas,
	Ciara Power, Brian Archbold

Hi,

On Wed, Oct 03, 2018 at 06:36:03PM +0100, Kevin Laatz wrote:
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <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);
>  

thanks for introducing this, I think this can be useful.
However, this deserves its own commit.

> +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).

> +	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.

> +	rte_lib_init_fn lib_init;

Considering the more generic "rte_param" name,
"rte_param_cb cb;" might be more suited.

> +	int enabled;
> +} rte_lib_init_params;
> +
> +/**
> + * @internal Register a libraries init function
> + *

This API is not EAL internal, it is public API.

> + * @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);

rte_param_register()

> +
>  #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.

>  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.

>  		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.

>  			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.

>  /* 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.

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

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.

> +
> +	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.

> +
> +	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.

> +
> +	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.

> + *
> + * @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

I think there have already been a remark on it, but why int32_t?
Errno is evaluated to an int type.

> +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;

volatile to disable possible optimization on the while loop?

> +	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
6WIND

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-04 13:25   ` Van Haaren, Harry
@ 2018-10-04 15:16     ` Gaëtan Rivet
  2018-10-04 15:53     ` Thomas Monjalon
  1 sibling, 0 replies; 220+ messages in thread
From: Gaëtan Rivet @ 2018-10-04 15:16 UTC (permalink / raw)
  To: Van Haaren, Harry
  Cc: Laatz, Kevin, dev, stephen, shreyansh.jain, thomas, Richardson, Bruce

Hi Harry,

On Thu, Oct 04, 2018 at 01:25:51PM +0000, Van Haaren, Harry wrote:
> > -----Original Message-----
> > From: Van Haaren, Harry
> > Sent: Thursday, October 4, 2018 2:00 PM
> > To: Laatz, Kevin <kevin.laatz@intel.com>; dev@dpdk.org
> > Cc: stephen@networkplumber.org; gaetan.rivet@6wind.com;
> > shreyansh.jain@nxp.com; thomas@monjalon.net; Richardson, Bruce
> > <bruce.richardson@intel.com>
> > Subject: RE: [PATCH v2 00/10] introduce telemetry library
> > 
> > > -----Original Message-----
> > > From: Laatz, Kevin
> > > Sent: Wednesday, October 3, 2018 6:36 PM
> > > To: dev@dpdk.org
> > > Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> > > stephen@networkplumber.org; gaetan.rivet@6wind.com;
> > shreyansh.jain@nxp.com;
> > > thomas@monjalon.net; Laatz, Kevin <kevin.laatz@intel.com>
> > > 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.
> > >
> > > ---
> > > 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
> > 
> > 
> > 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-04 13:25   ` Van Haaren, Harry
  2018-10-04 15:16     ` Gaëtan Rivet
@ 2018-10-04 15:53     ` Thomas Monjalon
  2018-10-05 22:05       ` Gaëtan Rivet
  2018-10-09 10:33       ` Van Haaren, Harry
  1 sibling, 2 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-04 15:53 UTC (permalink / raw)
  To: Van Haaren, Harry
  Cc: Laatz, Kevin, dev, stephen, gaetan.rivet, shreyansh.jain,
	Richardson, Bruce

04/10/2018 15:25, Van Haaren, Harry:
> From: Van Haaren, Harry
> > From: Laatz, Kevin
> > >
> > > 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.
> > >
> > > ---
> > > 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
> > 
> > 
> > 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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-04 15:53     ` Thomas Monjalon
@ 2018-10-05 22:05       ` Gaëtan Rivet
  2018-10-09 10:33       ` Van Haaren, Harry
  1 sibling, 0 replies; 220+ messages in thread
From: Gaëtan Rivet @ 2018-10-05 22:05 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Van Haaren, Harry, Laatz, Kevin, dev, stephen, shreyansh.jain,
	Richardson, Bruce

On Thu, Oct 04, 2018 at 05:53:53PM +0200, Thomas Monjalon wrote:
> 04/10/2018 15:25, Van Haaren, Harry:
> > From: Van Haaren, Harry
> > > From: Laatz, Kevin
> > > >
> > > > 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.
> > > >
> > > > ---
> > > > 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
> > > 
> > > 
> > > 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-04 15:53     ` Thomas Monjalon
  2018-10-05 22:05       ` Gaëtan Rivet
@ 2018-10-09 10:33       ` Van Haaren, Harry
  2018-10-09 11:41         ` Thomas Monjalon
  1 sibling, 1 reply; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-09 10:33 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Laatz, Kevin, dev, stephen, gaetan.rivet, shreyansh.jain,
	Richardson, Bruce

> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Thursday, October 4, 2018 4:54 PM
> To: Van Haaren, Harry <harry.van.haaren@intel.com>
> Cc: Laatz, Kevin <kevin.laatz@intel.com>; dev@dpdk.org;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> Richardson, Bruce <bruce.richardson@intel.com>
> Subject: Re: [PATCH v2 00/10] introduce telemetry library
> 
> 04/10/2018 15:25, Van Haaren, Harry:
> > From: Van Haaren, Harry
> > > From: Laatz, Kevin
> > > >
> > > > 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.
> > > >
> > > > ---
> > > > 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
> > >
> > >
> > > 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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-09 10:33       ` Van Haaren, Harry
@ 2018-10-09 11:41         ` Thomas Monjalon
  2018-10-09 14:56           ` Bruce Richardson
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-09 11:41 UTC (permalink / raw)
  To: Van Haaren, Harry
  Cc: Laatz, Kevin, dev, stephen, gaetan.rivet, shreyansh.jain,
	Richardson, Bruce

09/10/2018 12:33, Van Haaren, Harry:
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> > 04/10/2018 15:25, Van Haaren, Harry:
> > > From: Van Haaren, Harry
> > > > From: Laatz, Kevin
> > > > >
> > > > > 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.
> > > > >
> > > > > ---
> > > > > 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
> > > >
> > > >
> > > > 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.

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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-09 11:41         ` Thomas Monjalon
@ 2018-10-09 14:56           ` Bruce Richardson
  2018-10-09 17:07             ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Bruce Richardson @ 2018-10-09 14:56 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Van Haaren, Harry, Laatz, Kevin, dev, stephen, gaetan.rivet,
	shreyansh.jain

On Tue, Oct 09, 2018 at 01:41:10PM +0200, Thomas Monjalon wrote:
> 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.
> 

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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v2 00/10] introduce telemetry library
  2018-10-09 14:56           ` Bruce Richardson
@ 2018-10-09 17:07             ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-09 17:07 UTC (permalink / raw)
  To: Bruce Richardson
  Cc: Van Haaren, Harry, Laatz, Kevin, dev, stephen, gaetan.rivet,
	shreyansh.jain

09/10/2018 16:56, Bruce Richardson:
> On Tue, Oct 09, 2018 at 01:41:10PM +0200, Thomas Monjalon wrote:
> > 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.
> > 
> 
> 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.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v3 00/12] introduce telemetry library
  2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
                     ` (11 preceding siblings ...)
  2018-10-04 13:25   ` Van Haaren, Harry
@ 2018-10-10 10:51   ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 01/12] eal: add param register infrastructure Kevin Laatz
                       ` (12 more replies)
  12 siblings, 13 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v3 01/12] eal: add param register infrastructure
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 12:28       ` Thomas Monjalon
  2018-10-10 10:51     ` [PATCH v3 02/12] telemetry: initial telemetry infrastructure Kevin Laatz
                       ` (11 subsequent siblings)
  12 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 02/12] telemetry: initial telemetry infrastructure
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 01/12] eal: add param register infrastructure Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 03/12] telemetry: add initial connection socket Kevin Laatz
                       ` (10 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 03/12] telemetry: add initial connection socket
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 01/12] eal: add param register infrastructure Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 02/12] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 12:24       ` Thomas Monjalon
  2018-10-10 10:51     ` [PATCH v3 04/12] telemetry: add client feature and sockets Kevin Laatz
                       ` (9 subsequent siblings)
  12 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 04/12] telemetry: add client feature and sockets
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (2 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 03/12] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 05/12] telemetry: add parser for client socket messages Kevin Laatz
                       ` (8 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 05/12] telemetry: add parser for client socket messages
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (3 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 04/12] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 06/12] telemetry: update metrics before sending stats Kevin Laatz
                       ` (7 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 06/12] telemetry: update metrics before sending stats
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (4 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 05/12] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 07/12] telemetry: format json response when " Kevin Laatz
                       ` (6 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 07/12] telemetry: format json response when sending stats
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (5 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 06/12] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 08/12] telemetry: add tests for telemetry api Kevin Laatz
                       ` (5 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 08/12] telemetry: add tests for telemetry api
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (6 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 07/12] telemetry: format json response when " Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 09/12] telemetry: add ability to disable selftest Kevin Laatz
                       ` (4 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 09/12] telemetry: add ability to disable selftest
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (7 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 08/12] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 10/12] doc: add telemetry documentation Kevin Laatz
                       ` (3 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 10/12] doc: add telemetry documentation
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (8 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 09/12] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 11/12] usertools: add client python script for telemetry Kevin Laatz
                       ` (2 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 11/12] usertools: add client python script for telemetry
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (9 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 10/12] doc: add telemetry documentation Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-10 10:51     ` [PATCH v3 12/12] build: add dependency on telemetry to apps in meson Kevin Laatz
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v3 12/12] build: add dependency on telemetry to apps in meson
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (10 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 11/12] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-10 10:51     ` Kevin Laatz
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
  12 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-10 10:51 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v3 03/12] telemetry: add initial connection socket
  2018-10-10 10:51     ` [PATCH v3 03/12] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-10 12:24       ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-10 12:24 UTC (permalink / raw)
  To: Kevin Laatz, Ciara Power
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson, Brian Archbold

10/10/2018 12:51, Kevin Laatz:
> --- 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()

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v3 01/12] eal: add param register infrastructure
  2018-10-10 10:51     ` [PATCH v3 01/12] eal: add param register infrastructure Kevin Laatz
@ 2018-10-10 12:28       ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-10 12:28 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

10/10/2018 12:51, Kevin Laatz:
> --- /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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v4 00/13] introduce telemetry library
  2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
                       ` (11 preceding siblings ...)
  2018-10-10 10:51     ` [PATCH v3 12/12] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-11 16:58     ` Kevin Laatz
  2018-10-11 16:58       ` [PATCH v4 01/13] eal: add param register infrastructure Kevin Laatz
                         ` (13 more replies)
  12 siblings, 14 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v4 01/13] eal: add param register infrastructure
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-16 13:42         ` Thomas Monjalon
  2018-10-11 16:58       ` [PATCH v4 02/13] eal: make get runtime dir function public Kevin Laatz
                         ` (12 subsequent siblings)
  13 siblings, 2 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 02/13] eal: make get runtime dir function public
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
  2018-10-11 16:58       ` [PATCH v4 01/13] eal: add param register infrastructure Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
                         ` (11 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 03/13] telemetry: initial telemetry infrastructure
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
  2018-10-11 16:58       ` [PATCH v4 01/13] eal: add param register infrastructure Kevin Laatz
  2018-10-11 16:58       ` [PATCH v4 02/13] eal: make get runtime dir function public Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 04/13] telemetry: add initial connection socket Kevin Laatz
                         ` (10 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 04/13] telemetry: add initial connection socket
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (2 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 05/13] telemetry: add client feature and sockets Kevin Laatz
                         ` (9 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 05/13] telemetry: add client feature and sockets
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (3 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 06/13] telemetry: add parser for client socket messages Kevin Laatz
                         ` (8 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 06/13] telemetry: add parser for client socket messages
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (4 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 07/13] telemetry: update metrics before sending stats Kevin Laatz
                         ` (7 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 07/13] telemetry: update metrics before sending stats
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (5 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 06/13] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 08/13] telemetry: format json response when " Kevin Laatz
                         ` (6 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 08/13] telemetry: format json response when sending stats
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (6 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 07/13] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:46         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 09/13] telemetry: add tests for telemetry api Kevin Laatz
                         ` (5 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 09/13] telemetry: add tests for telemetry api
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (7 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 08/13] telemetry: format json response when " Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:46         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 10/13] telemetry: add ability to disable selftest Kevin Laatz
                         ` (4 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 10/13] telemetry: add ability to disable selftest
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (8 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 09/13] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:47         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 11/13] doc: add telemetry documentation Kevin Laatz
                         ` (3 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 11/13] doc: add telemetry documentation
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (9 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 10/13] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:47         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 12/13] usertools: add client python script for telemetry Kevin Laatz
                         ` (2 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 12/13] usertools: add client python script for telemetry
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (10 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:47         ` Van Haaren, Harry
  2018-10-11 16:58       ` [PATCH v4 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v4 13/13] build: add dependency on telemetry to apps in meson
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (11 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 12/13] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-11 16:58       ` Kevin Laatz
  2018-10-16  0:47         ` Van Haaren, Harry
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-11 16:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 01/13] eal: add param register infrastructure
  2018-10-11 16:58       ` [PATCH v4 01/13] eal: add param register infrastructure Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  2018-10-16 13:42         ` Thomas Monjalon
  1 sibling, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:58 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> 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.
> 
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 02/13] eal: make get runtime dir function public
  2018-10-11 16:58       ` [PATCH v4 02/13] eal: make get runtime dir function public Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:58 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> 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.
> 
> Signed-off-by: Kevin Laatz <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.
> ---

There's a minor rebase on .map file, but that's unavoidable with in-flight patchsets:

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 03/13] telemetry: initial telemetry infrastructure
  2018-10-11 16:58       ` [PATCH v4 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:58 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 03/13] telemetry: initial telemetry infrastructure
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

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 <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 04/13] telemetry: add initial connection socket
  2018-10-11 16:58       ` [PATCH v4 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:58 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 04/13] telemetry: add initial connection socket
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

<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 <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 05/13] telemetry: add client feature and sockets
  2018-10-11 16:58       ` [PATCH v4 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:58 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 05/13] telemetry: add client feature and sockets
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 06/13] telemetry: add parser for client socket messages
  2018-10-11 16:58       ` [PATCH v4 06/13] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 06/13] telemetry: add parser for client socket messages
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 07/13] telemetry: update metrics before sending stats
  2018-10-11 16:58       ` [PATCH v4 07/13] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-16  0:45         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:45 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 07/13] telemetry: update metrics before sending stats
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 08/13] telemetry: format json response when sending stats
  2018-10-11 16:58       ` [PATCH v4 08/13] telemetry: format json response when " Kevin Laatz
@ 2018-10-16  0:46         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:46 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 08/13] telemetry: format json response when sending stats
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 09/13] telemetry: add tests for telemetry api
  2018-10-11 16:58       ` [PATCH v4 09/13] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-16  0:46         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:46 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 09/13] telemetry: add tests for telemetry api
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 10/13] telemetry: add ability to disable selftest
  2018-10-11 16:58       ` [PATCH v4 10/13] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-16  0:47         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:47 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 10/13] telemetry: add ability to disable selftest
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 11/13] doc: add telemetry documentation
  2018-10-11 16:58       ` [PATCH v4 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-16  0:47         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:47 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 11/13] doc: add telemetry documentation
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 12/13] usertools: add client python script for telemetry
  2018-10-11 16:58       ` [PATCH v4 12/13] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-16  0:47         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:47 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce, Ciara Power, Brian Archbold

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [PATCH v4 12/13] usertools: add client python script for telemetry
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 13/13] build: add dependency on telemetry to apps in meson
  2018-10-11 16:58       ` [PATCH v4 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-16  0:47         ` Van Haaren, Harry
  0 siblings, 0 replies; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-16  0:47 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: stephen, gaetan.rivet, shreyansh.jain, thomas, mattias.ronnblom,
	Richardson, Bruce

> -----Original Message-----
> From: Laatz, Kevin
> Sent: Thursday, October 11, 2018 9:59 AM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> thomas@monjalon.net; mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> 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.
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> ---

Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 01/13] eal: add param register infrastructure
  2018-10-11 16:58       ` [PATCH v4 01/13] eal: add param register infrastructure Kevin Laatz
  2018-10-16  0:45         ` Van Haaren, Harry
@ 2018-10-16 13:42         ` Thomas Monjalon
  2018-10-16 14:20           ` Laatz, Kevin
  1 sibling, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-16 13:42 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

Hi,

11/10/2018 18:58, Kevin Laatz:
> 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.

Let's try to have a clear documentation for this new infra.

> +/**
> + * @file
> + *
> + * This API introduces the ability to register callbacks with EAL.  When

You should explain when the callback is called, and what is the role of
the callback.

> + * 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

What do you call a flag? Are you talking about an option to be parsed?

> + * 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.

You need to explain what is the circular dependency issue.

[...]
> +struct rte_param {

Please add a global comment for this struct, explain what it represents.
> +	TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
> +	char *eal_flag;              /** The EAL flag */

eal_flag, The EAL flag
Hum...
Please use different words to explain what is a flag.
If it is something to be parsed by getopt, it should be called
an option, not a flag.

> +	char *help_text;             /** Help text for the callback */

What the help text is used for? When is it printed?

> +	rte_param_cb cb;             /** Function pointer of the callback */
> +	int enabled;                 /** Enabled flag, should be 0 by default */

What means enabled in this context?

> +};
> +
> +/**
> + * @internal Check if the passed flag is valid
> + *
> + * @param flag
> + *  The flag to be parsed

Here too, you need to be more explicit about flag meaning.

> + *
> + * @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

No, this is not a helpful comment.

> + */
> +void __rte_experimental
> +rte_param_register(struct rte_param *reg_param);

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 01/13] eal: add param register infrastructure
  2018-10-16 13:42         ` Thomas Monjalon
@ 2018-10-16 14:20           ` Laatz, Kevin
  2018-10-16 15:13             ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-16 14:20 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

Hi Thomas,

Thanks for reviewing, see replies below.


On 16/10/2018 14:42, Thomas Monjalon wrote:
> Hi,
>
> 11/10/2018 18:58, Kevin Laatz:
>> 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.
> Let's try to have a clear documentation for this new infra.
Are you looking for a doc file here? Or just better Doxygen comments?

>
>> +/**
>> + * @file
>> + *
>> + * This API introduces the ability to register callbacks with EAL.  When
> You should explain when the callback is called, and what is the role of
> the callback.
I explained this below with telemetry as an example, maybe it needs to 
be extended a little.
>
>> + * 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
> What do you call a flag? Are you talking about an option to be parsed?
Agreed. The wording was unfortunate here. Will change to 'option' to 
make it clear it is related to getopt
>
>> + * 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.
> You need to explain what is the circular dependency issue.
I wrote the Doxygen trying to keep it generic. I can make it telemetry 
specific if it will be clearer?
>
> [...]
>> +struct rte_param {
> Please add a global comment for this struct, explain what it represents.
Good spot, thanks.
>> +	TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
>> +	char *eal_flag;              /** The EAL flag */
> eal_flag, The EAL flag
> Hum...
> Please use different words to explain what is a flag.
> If it is something to be parsed by getopt, it should be called
> an option, not a flag.
>
>> +	char *help_text;             /** Help text for the callback */
> What the help text is used for? When is it printed?
The help text is currently not being used. It was added speculatively.
>
>> +	rte_param_cb cb;             /** Function pointer of the callback */
>> +	int enabled;                 /** Enabled flag, should be 0 by default */
> What means enabled in this context?
Enabled means that the option, for example --telemetry, was passed and 
that we need to call the
callback at the end of EAL init. If the option was not passed for a 
given callback, then the callback
will not be called.
>
>> +};
>> +
>> +/**
>> + * @internal Check if the passed flag is valid
>> + *
>> + * @param flag
>> + *  The flag to be parsed
> Here too, you need to be more explicit about flag meaning.
>
>> + *
>> + * @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
> No, this is not a helpful comment.
>
>> + */
>> +void __rte_experimental
>> +rte_param_register(struct rte_param *reg_param);
>
>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v4 01/13] eal: add param register infrastructure
  2018-10-16 14:20           ` Laatz, Kevin
@ 2018-10-16 15:13             ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-16 15:13 UTC (permalink / raw)
  To: Laatz, Kevin
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

16/10/2018 16:20, Laatz, Kevin:
> Hi Thomas,
> 
> Thanks for reviewing, see replies below.
> 
> 
> On 16/10/2018 14:42, Thomas Monjalon wrote:
> > Hi,
> >
> > 11/10/2018 18:58, Kevin Laatz:
> >> 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.
> > Let's try to have a clear documentation for this new infra.
> Are you looking for a doc file here? Or just better Doxygen comments?

Just better doxygen comments.

> >> +/**
> >> + * @file
> >> + *
> >> + * This API introduces the ability to register callbacks with EAL.  When
> > You should explain when the callback is called, and what is the role of
> > the callback.
> I explained this below with telemetry as an example, maybe it needs to 
> be extended a little.
> >
> >> + * 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
> > What do you call a flag? Are you talking about an option to be parsed?
> Agreed. The wording was unfortunate here. Will change to 'option' to 
> make it clear it is related to getopt

OK, so you may need to change the file name, and struct/variable names.

> >> + * 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.
> > You need to explain what is the circular dependency issue.
> I wrote the Doxygen trying to keep it generic. I can make it telemetry 
> specific if it will be clearer?

No need to be specific. You can explain the library uses EAL and
is initialized by EAL too.

> > [...]
> >> +struct rte_param {
> > Please add a global comment for this struct, explain what it represents.
> Good spot, thanks.

> >> +	TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
> >> +	char *eal_flag;              /** The EAL flag */
> > eal_flag, The EAL flag
> > Hum...
> > Please use different words to explain what is a flag.
> > If it is something to be parsed by getopt, it should be called
> > an option, not a flag.
> >
> >> +	char *help_text;             /** Help text for the callback */
> > What the help text is used for? When is it printed?
> The help text is currently not being used. It was added speculatively.

Please do not add things speculatively.

> >> +	rte_param_cb cb;             /** Function pointer of the callback */
> >> +	int enabled;                 /** Enabled flag, should be 0 by default */
> > What means enabled in this context?
> Enabled means that the option, for example --telemetry, was passed and 
> that we need to call the
> callback at the end of EAL init. If the option was not passed for a 
> given callback, then the callback
> will not be called.

OK, you need to explain it in the comment.

> >> +};
> >> +
> >> +/**
> >> + * @internal Check if the passed flag is valid
> >> + *
> >> + * @param flag
> >> + *  The flag to be parsed
> > Here too, you need to be more explicit about flag meaning.
> >
> >> + *
> >> + * @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
> > No, this is not a helpful comment.
> >
> >> + */
> >> +void __rte_experimental
> >> +rte_param_register(struct rte_param *reg_param);

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v5 00/13] introduce telemetry library
  2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
                         ` (12 preceding siblings ...)
  2018-10-11 16:58       ` [PATCH v4 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-16 15:57       ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 01/13] eal: add param register infrastructure Kevin Laatz
                           ` (14 more replies)
  13 siblings, 15 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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

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

v5:
  - Moved the BUF_SIZE define to fix build (Harry)
  - Set default config for telemetry to 'n' (Harry)
  - Improved Doxygen comments (Thomas)
  - Cleaned up rte_param struct (Thomas)

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         |   91 ++
 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              | 1810 +++++++++++++++++++++
 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, 3618 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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-17  9:41           ` Thomas Monjalon
  2018-10-17 15:56           ` Gaëtan Rivet
  2018-10-16 15:57         ` [PATCH v5 02/13] eal: make get runtime dir function public Kevin Laatz
                           ` (13 subsequent siblings)
  14 siblings, 2 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 | 91 +++++++++++++++++++++++++++++++
 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, 178 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..a0635f7
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_param.h
@@ -0,0 +1,91 @@
+/* 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 an option as part
+ * of the struct used to register. If the option is passed to EAL when
+ * running 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 issues
+ * between EAL and the library. The library uses EAL but is also initialized by
+ * EAL. Hence, EAL depends on the init function of the library. The API
+ * introduced in rte_param allows us to register the library init with EAL
+ * (passing a function pointer) and avoid the circular dependency.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_param_cb)(void);
+
+/*
+ * Structure containing parameters relating to the function being registered
+ * with EAL.
+ */
+struct rte_param {
+	TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
+	char *eal_option;            /** The EAL option */
+	rte_param_cb cb;             /** Function pointer of the callback */
+
+	/**
+	 * Enabled flag, should be 0 by default. This is set when the option
+	 * for the callback is passed to EAL and determines if the callback is
+	 * called or not.
+	 */
+	int enabled;
+};
+
+/**
+ * @internal Check if the passed option is valid
+ *
+ * @param option
+ *  The option to be parsed
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -1 on fail
+ */
+int
+rte_param_parse(char *option);
+
+/**
+ * @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
+ *  Structure containing various params used to register the function.
+ */
+void __rte_experimental
+rte_param_register(struct rte_param *reg_param);
+
+/**
+ * @internal Iterate through the registered params and call the callback for
+ * 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..317e371
--- /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 *option)
+{
+	/* Check if the option is in the registered inits */
+	TAILQ_FOREACH(param, &rte_param_list, next) {
+		if (strcmp(option, param->eal_option) == 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 02/13] eal: make get runtime dir function public
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 01/13] eal: add param register infrastructure Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
                           ` (12 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 03/13] telemetry: initial telemetry infrastructure
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 01/13] eal: add param register infrastructure Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 02/13] eal: make get runtime dir function public Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 04/13] telemetry: add initial connection socket Kevin Laatz
                           ` (11 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 option. 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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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           | 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 +
 10 files changed, 235 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..f561eb4 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=n
+
+#
 # 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..b31b57d
--- /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_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_option = "--telemetry",
+	.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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 04/13] telemetry: add initial connection socket
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (2 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 05/13] telemetry: add client feature and sockets Kevin Laatz
                           ` (10 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_telemetry/rte_telemetry.c          | 226 ++++++++++++++++++++++++++
 lib/librte_telemetry/rte_telemetry_internal.h |   4 +
 2 files changed, 230 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index b31b57d..8d5754e 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,163 @@
  */
 
 #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 BUF_SIZE 1024
 #define SLEEP_TIME 10
 
 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 +167,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 +196,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 +278,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 +305,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 05/13] telemetry: add client feature and sockets
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (3 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 06/13] telemetry: add parser for client socket messages Kevin Laatz
                           ` (9 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_telemetry/meson.build              |   2 +
 lib/librte_telemetry/rte_telemetry.c          | 369 +++++++++++++++++++++++++-
 lib/librte_telemetry/rte_telemetry_internal.h |  25 ++
 mk/rte.app.mk                                 |   2 +-
 4 files changed, 394 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 8d5754e..9ed8ea6 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>
@@ -18,6 +19,7 @@
 #include "rte_telemetry_internal.h"
 
 #define BUF_SIZE 1024
+#define ACTION_POST 1
 #define SLEEP_TIME 10
 
 static telemetry_impl *static_telemetry;
@@ -39,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;
 }
 
@@ -115,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;
 	}
 
@@ -131,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;
@@ -141,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);
@@ -151,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;
@@ -173,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;
 }
 
@@ -285,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,
@@ -302,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) {
@@ -323,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 06/13] telemetry: add parser for client socket messages
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (4 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 07/13] telemetry: update metrics before sending stats Kevin Laatz
                           ` (8 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 9ed8ea6..9af855a 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 07/13] telemetry: update metrics before sending stats
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (5 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 06/13] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 08/13] telemetry: format json response when " Kevin Laatz
                           ` (7 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 9af855a..cd2bd12 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 08/13] telemetry: format json response when sending stats
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (6 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 07/13] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 09/13] telemetry: add tests for telemetry api Kevin Laatz
                           ` (6 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 cd2bd12..122640a 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 09/13] telemetry: add tests for telemetry api
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (7 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 08/13] telemetry: format json response when " Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:57         ` [PATCH v5 10/13] telemetry: add ability to disable selftest Kevin Laatz
                           ` (5 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 122640a..82b46a5 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 10/13] telemetry: add ability to disable selftest
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (8 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 09/13] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-16 15:57         ` Kevin Laatz
  2018-10-16 15:58         ` [PATCH v5 11/13] doc: add telemetry documentation Kevin Laatz
                           ` (4 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:57 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 82b46a5..7d97206 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 11/13] doc: add telemetry documentation
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (9 preceding siblings ...)
  2018-10-16 15:57         ` [PATCH v5 10/13] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-16 15:58         ` Kevin Laatz
  2018-10-16 15:58         ` [PATCH v5 12/13] usertools: add client python script for telemetry Kevin Laatz
                           ` (3 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 12/13] usertools: add client python script for telemetry
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (10 preceding siblings ...)
  2018-10-16 15:58         ` [PATCH v5 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-16 15:58         ` Kevin Laatz
  2018-10-16 15:58         ` [PATCH v5 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
                           ` (2 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v5 13/13] build: add dependency on telemetry to apps in meson
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (11 preceding siblings ...)
  2018-10-16 15:58         ` [PATCH v5 12/13] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-16 15:58         ` Kevin Laatz
  2018-10-18  8:07         ` [PATCH v5 00/13] introduce telemetry library Mattias Rönnblom
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-16 15:58 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-16 15:57         ` [PATCH v5 01/13] eal: add param register infrastructure Kevin Laatz
@ 2018-10-17  9:41           ` Thomas Monjalon
  2018-10-17 11:45             ` Gaëtan Rivet
  2018-10-17 15:56           ` Gaëtan Rivet
  1 sibling, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-17  9:41 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

I still think all the wording is incorrect.
Please start by describing what is "param", "flag" and "option" in your mind.
They are all mentioned in this file.
Are you sure rte_param is the right name?


16/10/2018 17:57, Kevin Laatz:
> --- /dev/null
> +++ b/lib/librte_eal/common/include/rte_param.h
> @@ -0,0 +1,91 @@
> +/* 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 an option as part
> + * of the struct used to register. If the option is passed to EAL when
> + * running 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 issues
> + * between EAL and the library. The library uses EAL but is also initialized by
> + * EAL. Hence, EAL depends on the init function of the library. The API
> + * introduced in rte_param allows us to register the library init with EAL
> + * (passing a function pointer) and avoid the circular dependency.
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +typedef int (*rte_param_cb)(void);
> +
> +/*
> + * Structure containing parameters relating to the function being registered
> + * with EAL.
> + */
> +struct rte_param {
> +	TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
> +	char *eal_option;            /** The EAL option */
> +	rte_param_cb cb;             /** Function pointer of the callback */
> +
> +	/**
> +	 * Enabled flag, should be 0 by default. This is set when the option
> +	 * for the callback is passed to EAL and determines if the callback is
> +	 * called or not.
> +	 */
> +	int enabled;
> +};
> +
> +/**
> + * @internal Check if the passed option is valid
> + *
> + * @param option
> + *  The option to be parsed
> + *
> + * @return
> + *  0 on success
> + * @return
> + *  -1 on fail
> + */
> +int
> +rte_param_parse(char *option);
> +
> +/**
> + * @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
> + *  Structure containing various params used to register the function.
> + */
> +void __rte_experimental
> +rte_param_register(struct rte_param *reg_param);
> +
> +/**
> + * @internal Iterate through the registered params and call the callback for
> + * the enabled ones.
> + *
> + * @return
> + *  0  on success
> + * @return
> + *  -1 on fail
> + */
> +int
> +rte_param_init(void);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
[...]
> --- /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 *option)
> +{
> +	/* Check if the option is in the registered inits */
> +	TAILQ_FOREACH(param, &rte_param_list, next) {
> +		if (strcmp(option, param->eal_option) == 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;
> +}

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-17  9:41           ` Thomas Monjalon
@ 2018-10-17 11:45             ` Gaëtan Rivet
  2018-10-17 13:46               ` Thomas Monjalon
  2018-10-17 13:55               ` Laatz, Kevin
  0 siblings, 2 replies; 220+ messages in thread
From: Gaëtan Rivet @ 2018-10-17 11:45 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Kevin Laatz, dev, harry.van.haaren, stephen, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

Hi Thomas,

On Wed, Oct 17, 2018 at 11:41:53AM +0200, Thomas Monjalon wrote:
> I still think all the wording is incorrect.
> Please start by describing what is "param", "flag" and "option" in your mind.
> They are all mentioned in this file.
> Are you sure rte_param is the right name?
> 

I suggested the name rte_param, I think the original proposal was
rte_lib_init, which to me unduly diminished the intent of these structures.

I think rte_param seems proper, this is a generic parameter object
description. The integer "enabled" is described as a flag in the
structure, as it is used to flag the init routine down the road to
trigger the init callback associated with this parameter.

eal_option is reminiscent of optarg / optind of getopt() family,
which seems fitting.

I don't mean to overstep Kevin's role defending his work, but given
that I proposed some of this naming and pushed for this direction to be
taken in the first place, I feel I should help explain my propositions.

rte_param could become rte_parameter or rte_option instead, eal_option
could become opt_string or opt_str, and so on, do you have specific
ideas about proper names?

> 
> 16/10/2018 17:57, Kevin Laatz:
> > --- /dev/null
> > +++ b/lib/librte_eal/common/include/rte_param.h
> > @@ -0,0 +1,91 @@
> > +/* 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 an option as part
> > + * of the struct used to register. If the option is passed to EAL when
> > + * running 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.
> > + *

Citing --telemetry here is a bad idea, this file is lib-agnostic,
--telemetry is not assured to be relevant.

> > + * The register API can be used to resolve circular dependency issues
> > + * between EAL and the library. The library uses EAL but is also initialized by
> > + * EAL. Hence, EAL depends on the init function of the library. The API
> > + * introduced in rte_param allows us to register the library init with EAL
> > + * (passing a function pointer) and avoid the circular dependency.
> > + */

-- 
Gaëtan Rivet
6WIND

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-17 11:45             ` Gaëtan Rivet
@ 2018-10-17 13:46               ` Thomas Monjalon
  2018-10-17 14:09                 ` Laatz, Kevin
  2018-10-17 13:55               ` Laatz, Kevin
  1 sibling, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-17 13:46 UTC (permalink / raw)
  To: Gaëtan Rivet
  Cc: Kevin Laatz, dev, harry.van.haaren, stephen, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

17/10/2018 13:45, Gaëtan Rivet:
> Hi Thomas,
> 
> On Wed, Oct 17, 2018 at 11:41:53AM +0200, Thomas Monjalon wrote:
> > I still think all the wording is incorrect.
> > Please start by describing what is "param", "flag" and "option" in your mind.
> > They are all mentioned in this file.
> > Are you sure rte_param is the right name?
> > 
> 
> I suggested the name rte_param, I think the original proposal was
> rte_lib_init, which to me unduly diminished the intent of these structures.

I think the right word is "run-time option".
An option can have a parameter.
If this API is not supporting options with parameters, the name is
really misleading.

> I think rte_param seems proper, this is a generic parameter object
> description. The integer "enabled" is described as a flag in the
> structure, as it is used to flag the init routine down the road to
> trigger the init callback associated with this parameter.

"enabled" can be documented as the result of the option parsing.
If the option is given to rte_eal_init, it becomes enabled.

> eal_option is reminiscent of optarg / optind of getopt() family,
> which seems fitting.
> 
> I don't mean to overstep Kevin's role defending his work, but given
> that I proposed some of this naming and pushed for this direction to be
> taken in the first place, I feel I should help explain my propositions.
> 
> rte_param could become rte_parameter or rte_option instead, eal_option
> could become opt_string or opt_str, and so on, do you have specific
> ideas about proper names?

rte_option looks OK.

The global picture may be better explained I think.
Any help with wording and documentation is appreciated, thanks.


> > 16/10/2018 17:57, Kevin Laatz:
> > > --- /dev/null
> > > +++ b/lib/librte_eal/common/include/rte_param.h
> > > @@ -0,0 +1,91 @@
> > > +/* 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 an option as part
> > > + * of the struct used to register. If the option is passed to EAL when
> > > + * running 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.
> > > + *
> 
> Citing --telemetry here is a bad idea, this file is lib-agnostic,
> --telemetry is not assured to be relevant.
> 
> > > + * The register API can be used to resolve circular dependency issues
> > > + * between EAL and the library. The library uses EAL but is also initialized by
> > > + * EAL. Hence, EAL depends on the init function of the library. The API
> > > + * introduced in rte_param allows us to register the library init with EAL
> > > + * (passing a function pointer) and avoid the circular dependency.
> > > + */
> 
> 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-17 11:45             ` Gaëtan Rivet
  2018-10-17 13:46               ` Thomas Monjalon
@ 2018-10-17 13:55               ` Laatz, Kevin
  1 sibling, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-17 13:55 UTC (permalink / raw)
  To: Gaëtan Rivet, Thomas Monjalon
  Cc: dev, harry.van.haaren, stephen, shreyansh.jain, mattias.ronnblom,
	bruce.richardson

Hi Thomas,

On 17/10/2018 12:45, Gaëtan Rivet wrote:
> Hi Thomas,
>
> On Wed, Oct 17, 2018 at 11:41:53AM +0200, Thomas Monjalon wrote:
>> I still think all the wording is incorrect.
>> Please start by describing what is "param", "flag" and "option" in your mind.
>> They are all mentioned in this file.
>> Are you sure rte_param is the right name?
> I suggested the name rte_param, I think the original proposal was
> rte_lib_init, which to me unduly diminished the intent of these structures.
>
> I think rte_param seems proper, this is a generic parameter object
> description. The integer "enabled" is described as a flag in the
> structure, as it is used to flag the init routine down the road to
> trigger the init callback associated with this parameter.
The purpose of "enabled" is to avoid redundantly calling all of the 
callbacks in rte_param_list. Enabled should always be initialized to 0 
by the library (as described in the Doxygen comment) and it will be set 
if the 'option' for the callback is passed to EAL when running the 
application (this is done in rte_param_parse).
To avoid confusion, I have renamed eal_flag to eal_option in the param 
struct and reworded the Doxygen comments accordingly. The only place 
flag is mentioned now is for "enabled".
>
> eal_option is reminiscent of optarg / optind of getopt() family,
> which seems fitting.
>
> I don't mean to overstep Kevin's role defending his work, but given
> that I proposed some of this naming and pushed for this direction to be
> taken in the first place, I feel I should help explain my propositions.
>
> rte_param could become rte_parameter or rte_option instead, eal_option
> could become opt_string or opt_str, and so on, do you have specific
> ideas about proper names?
I think rte_param was an improvement on rte_lib_init, for reasons 
mentioned by Gaetan.
I am open to renaming it if you have any better, more descriptive name 
in mind?

  <snip>

Thanks,
Kevin

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-17 13:46               ` Thomas Monjalon
@ 2018-10-17 14:09                 ` Laatz, Kevin
  2018-10-17 14:20                   ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-17 14:09 UTC (permalink / raw)
  To: Thomas Monjalon, Gaëtan Rivet
  Cc: dev, harry.van.haaren, stephen, shreyansh.jain, mattias.ronnblom,
	bruce.richardson

Hi Thomas,


On 17/10/2018 14:46, Thomas Monjalon wrote:
> 17/10/2018 13:45, Gaëtan Rivet:
>> Hi Thomas,
>>
>> On Wed, Oct 17, 2018 at 11:41:53AM +0200, Thomas Monjalon wrote:
>>> I still think all the wording is incorrect.
>>> Please start by describing what is "param", "flag" and "option" in your mind.
>>> They are all mentioned in this file.
>>> Are you sure rte_param is the right name?
>>>
>> I suggested the name rte_param, I think the original proposal was
>> rte_lib_init, which to me unduly diminished the intent of these structures.
> I think the right word is "run-time option".
> An option can have a parameter.
> If this API is not supporting options with parameters, the name is
> really misleading.
The option will be passed like any other EAL option. Right now it 
doesn't support any parameters but in future we could add functionality 
like we had with the --vdev solution where we can pass selftest=1 with 
the telemetry option.
>
>> I think rte_param seems proper, this is a generic parameter object
>> description. The integer "enabled" is described as a flag in the
>> structure, as it is used to flag the init routine down the road to
>> trigger the init callback associated with this parameter.
> "enabled" can be documented as the result of the option parsing.
> If the option is given to rte_eal_init, it becomes enabled.
The Doxygen comments mention that the flag should initially be set to 0 
and will be set to 1 if the option for the relevant callback is passed 
to EAL when running your application.
>
>> eal_option is reminiscent of optarg / optind of getopt() family,
>> which seems fitting.
>>
>> I don't mean to overstep Kevin's role defending his work, but given
>> that I proposed some of this naming and pushed for this direction to be
>> taken in the first place, I feel I should help explain my propositions.
>>
>> rte_param could become rte_parameter or rte_option instead, eal_option
>> could become opt_string or opt_str, and so on, do you have specific
>> ideas about proper names?
> rte_option looks OK.
I'm happy to change it to rte_option if we have consensus on it :)
>
> The global picture may be better explained I think.
> Any help with wording and documentation is appreciated, thanks.

Thanks,
Kevin

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-17 14:09                 ` Laatz, Kevin
@ 2018-10-17 14:20                   ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-17 14:20 UTC (permalink / raw)
  To: Laatz, Kevin
  Cc: Gaëtan Rivet, dev, harry.van.haaren, stephen,
	shreyansh.jain, mattias.ronnblom, bruce.richardson

17/10/2018 16:09, Laatz, Kevin:
> Hi Thomas,
> 
> 
> On 17/10/2018 14:46, Thomas Monjalon wrote:
> > 17/10/2018 13:45, Gaëtan Rivet:
> >> Hi Thomas,
> >>
> >> On Wed, Oct 17, 2018 at 11:41:53AM +0200, Thomas Monjalon wrote:
> >>> I still think all the wording is incorrect.
> >>> Please start by describing what is "param", "flag" and "option" in your mind.
> >>> They are all mentioned in this file.
> >>> Are you sure rte_param is the right name?
> >>>
> >> I suggested the name rte_param, I think the original proposal was
> >> rte_lib_init, which to me unduly diminished the intent of these structures.
> > I think the right word is "run-time option".
> > An option can have a parameter.
> > If this API is not supporting options with parameters, the name is
> > really misleading.
> The option will be passed like any other EAL option. Right now it 
> doesn't support any parameters but in future we could add functionality 
> like we had with the --vdev solution where we can pass selftest=1 with 
> the telemetry option.
> >
> >> I think rte_param seems proper, this is a generic parameter object
> >> description. The integer "enabled" is described as a flag in the
> >> structure, as it is used to flag the init routine down the road to
> >> trigger the init callback associated with this parameter.
> > "enabled" can be documented as the result of the option parsing.
> > If the option is given to rte_eal_init, it becomes enabled.
> The Doxygen comments mention that the flag should initially be set to 0 
> and will be set to 1 if the option for the relevant callback is passed 
> to EAL when running your application.

Saying "passed to EAL" is too vague.
You need to differentiate the option parsing and the init.
Both are done in EAL, that's why you need to be more specific.

> >> eal_option is reminiscent of optarg / optind of getopt() family,
> >> which seems fitting.
> >>
> >> I don't mean to overstep Kevin's role defending his work, but given
> >> that I proposed some of this naming and pushed for this direction to be
> >> taken in the first place, I feel I should help explain my propositions.
> >>
> >> rte_param could become rte_parameter or rte_option instead, eal_option
> >> could become opt_string or opt_str, and so on, do you have specific
> >> ideas about proper names?
> > rte_option looks OK.
> I'm happy to change it to rte_option if we have consensus on it :)

OK, thanks

> > The global picture may be better explained I think.
> > Any help with wording and documentation is appreciated, thanks.
> 
> Thanks,
> Kevin
> 
> 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-16 15:57         ` [PATCH v5 01/13] eal: add param register infrastructure Kevin Laatz
  2018-10-17  9:41           ` Thomas Monjalon
@ 2018-10-17 15:56           ` Gaëtan Rivet
  2018-10-18 15:58             ` Laatz, Kevin
  1 sibling, 1 reply; 220+ messages in thread
From: Gaëtan Rivet @ 2018-10-17 15:56 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson

Some suggestions,

On Tue, Oct 16, 2018 at 04:57:50PM +0100, Kevin Laatz wrote:
> 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 <kevin.laatz@intel.com>
> Acked-by: Harry van Haaren <harry.van.haaren@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 | 91 +++++++++++++++++++++++++++++++
>  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, 178 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..a0635f7
> --- /dev/null
> +++ b/lib/librte_eal/common/include/rte_param.h
> @@ -0,0 +1,91 @@
> +/* 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 an option as part
> + * of the struct used to register. If the option is passed to EAL when
> + * running 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 issues
> + * between EAL and the library. The library uses EAL but is also initialized by
> + * EAL. Hence, EAL depends on the init function of the library. The API
> + * introduced in rte_param allows us to register the library init with EAL
> + * (passing a function pointer) and avoid the circular dependency.

This API offers the ability to register options to the EAL command line
and map those options to functions, that will be executed at the end of
EAL initialization. These options will be available as part of the EAL
command line of applications and are dynamically managed.

This is used primarily by DPDK libraries offering command line options.
Currently, this API is limited to registering options without argument.

> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +typedef int (*rte_param_cb)(void);
> +
> +/*

   /**

> + * Structure containing parameters relating to the function being registered
> + * with EAL.

   Structure describing the EAL command line option being registered.

> + */
> +struct rte_param {

As said earlier, rte_option instead.

> +	TAILQ_ENTRY(rte_param) next; /** The next entry in the TAILQ*/
                                    ^ missing a '<' here for doxygen,
                                    and a space after TAILQ.

                                 /**< Next entry. */
                                    or
                                 /**< Next entry in the list. */

                                 Also reads better, TAILQ is
                                 implementation detail, not too useful
                                 in API doc.
                                 Also missing a period.

> +	char *eal_option;            /** The EAL option */
         *opt_str; /**< The option name. */

> +	rte_param_cb cb;             /** Function pointer of the callback */
                 cb; /**< Function called when the option is used. */
> +
> +	/**
> +	 * Enabled flag, should be 0 by default. This is set when the option
> +	 * for the callback is passed to EAL and determines if the callback is
> +	 * called or not.
> +	 */
> +	int enabled;
                 /**< Set when the option is used. */
> +};
> +
> +/**
> + * @internal Check if the passed option is valid

Check if option is registered.

> + *
> + * @param option
> + *  The option to be parsed
> + *
> + * @return
> + *  0 on success
> + * @return
> + *  -1 on fail
> + */
> +int
> +rte_param_parse(char *option);

This should probably be a const char *.
Also, internal functions must not be part of public headers.
Move the prototype to eal_private.h instead.

> +
> +/**
> + * @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
> + *  Structure containing various params used to register the function.
> + */

Register an option to the EAL command line.
When recognized, the associated function will
be executed at the end of EAL initialization.

The associated structure must be available the whole time
this option is registered (i.e. not stack memory)

@param option
   Structure describing the option to parse.

> +void __rte_experimental
> +rte_param_register(struct rte_param *reg_param);
> +
> +/**
> + * @internal Iterate through the registered params and call the callback for
> + * the enabled ones.

Iterate through registered options and execute the associated callback
if enabled.

> + *
> + * @return
> + *  0  on success
> + * @return
> + *  -1 on fail
> + */
> +int
> +rte_param_init(void);

This function prototype should be added to eal_private.h instead.

Maybe missing: rte_option_unregister, to be executed in .FINI, if a
plugin is unloaded.

> +
> +#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..317e371
> --- /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 *option)
> +{
> +	/* Check if the option is in the registered inits */
> +	TAILQ_FOREACH(param, &rte_param_list, next) {
> +		if (strcmp(option, param->eal_option) == 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);

What happens when an option already exists in the list?

> +}
> +
> +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");

It would be better to be able to know which function failed
and why. "Callback failed" is not helpful to the user.
Maybe rte_option_init() return value should be expanded to allow
for error string, error value to be passed, that could come from the
library itself, or simply printing the option name that is the source of
the error.

> +		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
> 

Best regards,
-- 
Gaëtan Rivet
6WIND

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 00/13] introduce telemetry library
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (12 preceding siblings ...)
  2018-10-16 15:58         ` [PATCH v5 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-18  8:07         ` Mattias Rönnblom
  2018-10-19 10:16           ` Laatz, Kevin
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
  14 siblings, 1 reply; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-18  8:07 UTC (permalink / raw)
  To: Kevin Laatz, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson

Most of the issues I pointed out in v2 of this patchset is still here.

On 2018-10-16 17:57, Kevin Laatz wrote:
> 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
> 
> 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
> 
> v5:
>    - Moved the BUF_SIZE define to fix build (Harry)
>    - Set default config for telemetry to 'n' (Harry)
>    - Improved Doxygen comments (Thomas)
>    - Cleaned up rte_param struct (Thomas)
> 
> 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         |   91 ++
>   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              | 1810 +++++++++++++++++++++
>   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, 3618 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
> 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 01/13] eal: add param register infrastructure
  2018-10-17 15:56           ` Gaëtan Rivet
@ 2018-10-18 15:58             ` Laatz, Kevin
  0 siblings, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-18 15:58 UTC (permalink / raw)
  To: Gaëtan Rivet
  Cc: dev, harry.van.haaren, stephen, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson

Hi Gaetan,

Thanks for reviewing and providing suggestions.


On 17/10/2018 16:56, Gaëtan Rivet wrote:

>> +
>> +int
>> +rte_param_parse(char *option)
>> +{
>> +	/* Check if the option is in the registered inits */
>> +	TAILQ_FOREACH(param, &rte_param_list, next) {
>> +		if (strcmp(option, param->eal_option) == 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);
> What happens when an option already exists in the list?
Will add a check to avoid option duplication, good spot. Thanks
>> @@ -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");
> It would be better to be able to know which function failed
> and why. "Callback failed" is not helpful to the user.
> Maybe rte_option_init() return value should be expanded to allow
> for error string, error value to be passed, that could come from the
> library itself, or simply printing the option name that is the source of
> the error.
I agree that the error message is not helpful. Expanding the return 
value to pass the option name or more would be difficult however as we 
would need to check for success/fail of the execution of the callback in 
rte_option_init(). Since the we don't know what the return type of a 
given registered callback is, it is difficult to do the error checking here.
With that in mind, I think it might be best to leave the library's 
function do the error checking itself, and changing the return type of 
rte_option_init() to void. This way we could get library specific errors 
using RTE_LOG, for example. Thoughts?

Best regards,
Kevin

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 00/13] introduce telemetry library
  2018-10-18  8:07         ` [PATCH v5 00/13] introduce telemetry library Mattias Rönnblom
@ 2018-10-19 10:16           ` Laatz, Kevin
  2018-10-22  7:11             ` Mattias Rönnblom
  0 siblings, 1 reply; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-19 10:16 UTC (permalink / raw)
  To: Mattias Rönnblom, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson

Hi Mattias,

On 18/10/2018 09:07, Mattias Rönnblom wrote:
> Most of the issues I pointed out in v2 of this patchset is still here.

Will recheck feedback for the next version.

With regards to comments on v2, 3/10, could you please help provide 
clarification on the below?

<Copy from the v2 feedback>
On 03/10/2018 20:06, Mattias Rönnblom wrote:
> On 2018-10-03 19:36, Kevin Laatz wrote:
>> From: Ciara Power <ciara.power@intel.com>
>> +
>> +    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.
>
Is the suggestion here simply to use socket(AF_UNIX, SOCK_SEQPACKET) 
instead of (AF_UNIX, SOCK_STREAM) ?
>
>> +
>> +    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).
>
Will the above solve this part of the problem too?

Best regards,
Kevin

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 00/13] introduce telemetry library
  2018-10-19 10:16           ` Laatz, Kevin
@ 2018-10-22  7:11             ` Mattias Rönnblom
  2018-10-22  9:03               ` Laatz, Kevin
  0 siblings, 1 reply; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-22  7:11 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson

On 2018-10-19 12:16, Laatz, Kevin wrote:
> On 03/10/2018 20:06, Mattias Rönnblom wrote:
>> On 2018-10-03 19:36, Kevin Laatz wrote:
>>> From: Ciara Power <ciara.power@intel.com>
>>> +
>>> +    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.
>>
> Is the suggestion here simply to use socket(AF_UNIX, SOCK_SEQPACKET) 
> instead of (AF_UNIX, SOCK_STREAM) ?

That would be the most straight-forward to the problem, I think. Linux' 
SOCK_SEQPACKET implementation has problems with really large messages 
(since a per-message linear buffer is allocated), but I'm guessing these 
messages are not in the hundreds-of-kb range.

>>
>>> +
>>> +    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).
>>
> Will the above solve this part of the problem too?
> 

Yes. The kernel will delivered the application (at most) one message, in 
its entirety.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v5 00/13] introduce telemetry library
  2018-10-22  7:11             ` Mattias Rönnblom
@ 2018-10-22  9:03               ` Laatz, Kevin
  0 siblings, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-22  9:03 UTC (permalink / raw)
  To: Mattias Rönnblom, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson

Hi Mattias,

Thanks for the input and clarification. I will include these changes in 
the v6.

Regards,
Kevin

On 22/10/2018 08:11, Mattias Rönnblom wrote:
> On 2018-10-19 12:16, Laatz, Kevin wrote:
>> On 03/10/2018 20:06, Mattias Rönnblom wrote:
>>> On 2018-10-03 19:36, Kevin Laatz wrote:
>>>> From: Ciara Power <ciara.power@intel.com>
>>>> +
>>>> +    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.
>>>
>> Is the suggestion here simply to use socket(AF_UNIX, SOCK_SEQPACKET) 
>> instead of (AF_UNIX, SOCK_STREAM) ?
>
> That would be the most straight-forward to the problem, I think. 
> Linux' SOCK_SEQPACKET implementation has problems with really large 
> messages (since a per-message linear buffer is allocated), but I'm 
> guessing these messages are not in the hundreds-of-kb range.
>
>>>
>>>> +
>>>> +    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).
>>>
>> Will the above solve this part of the problem too?
>>
>
> Yes. The kernel will delivered the application (at most) one message, 
> in its entirety.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v6 00/13] introduce telemetry library
  2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
                           ` (13 preceding siblings ...)
  2018-10-18  8:07         ` [PATCH v5 00/13] introduce telemetry library Mattias Rönnblom
@ 2018-10-22 11:00         ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 01/13] eal: add option register infrastructure Kevin Laatz
                             ` (13 more replies)
  14 siblings, 14 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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

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

v5:
  - Moved the BUF_SIZE define to fix build (Harry)
  - Set default config for telemetry to 'n' (Harry)
  - Improved Doxygen comments (Thomas)
  - Cleaned up rte_param struct (Thomas)

v6:
  - Renamed rte_param to rte_option (Thomas)
  - Moved internal functions to eal_private.h (Gaetan)
  - Added fail check for pthread_attr_init() (Mattias)
  - Changed socket implementation to SOCK_SEQPACKET (Mattias)
  - Added check to avoid option duplicates (Gaetan)
  - Removed telemetry specifics from Doxygen comment (Gaetan)
  - General Doxygen clean-up (Thomas)
  - General code 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 (3):
  eal: add option 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                   |   16 +-
 lib/librte_eal/common/Makefile                    |    1 +
 lib/librte_eal/common/eal_filesystem.h            |   15 +-
 lib/librte_eal/common/eal_private.h               |   21 +
 lib/librte_eal/common/include/rte_eal.h           |    9 +
 lib/librte_eal/common/include/rte_option.h        |   63 +
 lib/librte_eal/common/meson.build                 |    2 +
 lib/librte_eal/common/rte_option.c                |   54 +
 lib/librte_eal/linuxapp/eal/Makefile              |    1 +
 lib/librte_eal/linuxapp/eal/eal.c                 |   16 +-
 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              | 1816 +++++++++++++++++++++
 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 ++
 39 files changed, 3617 insertions(+), 19 deletions(-)
 create mode 100644 doc/guides/howto/telemetry.rst
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v6 01/13] eal: add option register infrastructure
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 02/13] eal: make get runtime dir function public Kevin Laatz
                             ` (12 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_eal/bsdapp/eal/Makefile         |  1 +
 lib/librte_eal/bsdapp/eal/eal.c            | 14 ++++++-
 lib/librte_eal/common/Makefile             |  1 +
 lib/librte_eal/common/eal_private.h        | 21 ++++++++++
 lib/librte_eal/common/include/rte_option.h | 63 ++++++++++++++++++++++++++++++
 lib/librte_eal/common/meson.build          |  2 +
 lib/librte_eal/common/rte_option.c         | 54 +++++++++++++++++++++++++
 lib/librte_eal/linuxapp/eal/Makefile       |  1 +
 lib/librte_eal/linuxapp/eal/eal.c          | 14 ++++++-
 lib/librte_eal/rte_eal_version.map         |  1 +
 10 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d27da3d..03ead51 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_option.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..8904fe3 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -788,6 +797,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca6882..87d8c45 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_option.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/eal_private.h b/lib/librte_eal/common/eal_private.h
index 4f809a8..e633d7d 100644
--- a/lib/librte_eal/common/eal_private.h
+++ b/lib/librte_eal/common/eal_private.h
@@ -304,4 +304,25 @@ int
 rte_devargs_layers_parse(struct rte_devargs *devargs,
 			 const char *devstr);
 
+/**
+ * Check if the option is registered.
+ *
+ * @param option
+ *  The option to be parsed.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -1 on fail
+ */
+int
+rte_option_parse(const char *opt);
+
+/**
+ * Iterate through the registered options and execute the associated
+ * callback if enabled.
+ */
+void
+rte_option_init(void);
+
 #endif /* _EAL_PRIVATE_H_ */
diff --git a/lib/librte_eal/common/include/rte_option.h b/lib/librte_eal/common/include/rte_option.h
new file mode 100644
index 0000000..ae7c2d1
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_option.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_PARAM_H__
+#define __INCLUDE_RTE_PARAM_H__
+
+/**
+ * @file
+ *
+ * This API offers the ability to register options to the EAL command line and
+ * map those options to functions that will be executed at the end of EAL
+ * initialization. These options will be available as part of the EAL command
+ * line of applications and are dynamically managed.
+ *
+ * This is used primarily by DPDK libraries offering command line options.
+ * Currently, this API is limited to registering options without argument.
+ *
+ * The register API can be used to resolve circular dependency issues
+ * between EAL and the library. The library uses EAL, but is also initialized
+ * by EAL. Hence, EAL depends on the init function of the library. The API
+ * introduced in rte_option allows us to register the library init with EAL
+ * (passing a function pointer) and avoid the circular dependency.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_option_cb)(void);
+
+/*
+ * Structure describing the EAL command line option being registered.
+ */
+struct rte_option {
+	TAILQ_ENTRY(rte_option) next; /**< Next entry in the list. */
+	char *opt_str;             /**< The option name. */
+	rte_option_cb cb;          /**< Function called when option is used. */
+	int enabled;               /**< Set when the option is used. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register an option to the EAL command line.
+ * When recognized, the associated function will be executed at the end of EAL
+ * initialization.
+ *
+ * The associated structure must be available the whole time this option is
+ * registered (i.e. not stack memory).
+ *
+ * @param opt
+ *  Structure describing the option to parse.
+ */
+void __rte_experimental
+rte_option_register(struct rte_option *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index b7fc984..f879e65 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_option.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_option.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_option.c b/lib/librte_eal/common/rte_option.c
new file mode 100644
index 0000000..02d59a8
--- /dev/null
+++ b/lib/librte_eal/common/rte_option.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_option.h>
+
+#include "eal_private.h"
+
+TAILQ_HEAD(rte_option_list, rte_option);
+
+struct rte_option_list rte_option_list =
+	TAILQ_HEAD_INITIALIZER(rte_option_list);
+
+static struct rte_option *option;
+
+int
+rte_option_parse(const char *opt)
+{
+	/* Check if the option is registered */
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt, option->opt_str) == 0) {
+			option->enabled = 1;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void __rte_experimental
+rte_option_register(struct rte_option *opt)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt->opt_str, option->opt_str) == 0)
+			RTE_LOG(INFO, EAL, "Option %s has already been registered.",
+					opt->opt_str);
+			return;
+	}
+
+	TAILQ_INSERT_HEAD(&rte_option_list, opt, next);
+}
+
+void
+rte_option_init(void)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (option->enabled)
+			option->cb();
+	}
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 5c16bc4..d4df958 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_option.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..f0ad3aa 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -1071,6 +1080,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 73282bb..09e0816 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_option_register;
 	rte_service_lcore_attr_get;
 	rte_service_lcore_attr_reset_all;
 	rte_service_may_be_active;
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 02/13] eal: make get runtime dir function public
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 01/13] eal: add option register infrastructure Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
                             ` (11 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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  | 15 ++++++++-------
 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, 20 insertions(+), 9 deletions(-)

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 8904fe3..6101fdc 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..b3e8ae5 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,8 @@ 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 +67,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 +79,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 +100,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 f0ad3aa..1f6522c 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 09e0816..64f60ec 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 03/13] telemetry: initial telemetry infrastructure
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 01/13] eal: add option register infrastructure Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 02/13] eal: make get runtime dir function public Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 04/13] telemetry: add initial connection socket Kevin Laatz
                             ` (10 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 option. 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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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           | 123 +++++++++++++++++++++++++
 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, 241 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..f561eb4 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=n
+
+#
 # 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..ee9c90f
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,123 @@
+/* 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_option.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);
+
+	ret = pthread_attr_init(&attr);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Pthread attribute init failed");
+		return -EPERM;
+	}
+
+	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_option option = {
+	.opt_str = "--telemetry",
+	.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_option_register(&option);
+}
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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 04/13] telemetry: add initial connection socket
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (2 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 13:50             ` Mattias Rönnblom
  2018-10-22 11:00           ` [PATCH v6 05/13] telemetry: add client feature and sockets Kevin Laatz
                             ` (9 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_telemetry/rte_telemetry.c          | 226 ++++++++++++++++++++++++++
 lib/librte_telemetry/rte_telemetry_internal.h |   4 +
 2 files changed, 230 insertions(+)

diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index ee9c90f..10311be 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,163 @@
  */
 
 #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_option.h>
+#include <rte_string_fns.h>
 
 #include "rte_telemetry.h"
 #include "rte_telemetry_internal.h"
 
+#define BUF_SIZE 1024
 #define SLEEP_TIME 10
 
 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 +167,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 +196,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;
+	char socket_path[BUF_SIZE];
+
+	if (telemetry == NULL)
+		return -1;
+
+	telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (telemetry->server_fd == -1) {
+		TELEMETRY_LOG_ERR("Failed to open socket");
+		return -1;
+	}
+
+	ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		goto close_socket;
+	}
+
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+	unlink(socket_path);
+
+	if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
+		sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Socket binding error");
+		goto close_socket;
+	}
+
+	return 0;
+
+close_socket:
+	if (close(telemetry->server_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		free(telemetry);
+		return -EPERM;
+	}
+
+	return -1;
+}
+
 int32_t
 rte_telemetry_init()
 {
@@ -77,6 +285,14 @@ rte_telemetry_init()
 		return -EPERM;
 	}
 
+	ret = rte_telemetry_create_socket(static_telemetry);
+	if (ret < 0) {
+		ret = rte_telemetry_cleanup();
+		if (ret < 0)
+			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
+		return -EPERM;
+	}
+
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
 		(void *)static_telemetry);
@@ -95,11 +311,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 05/13] telemetry: add client feature and sockets
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (3 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 14:05             ` Mattias Rönnblom
  2018-10-22 11:00           ` [PATCH v6 06/13] telemetry: add parser for client socket messages Kevin Laatz
                             ` (8 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_telemetry/meson.build              |   2 +
 lib/librte_telemetry/rte_telemetry.c          | 369 +++++++++++++++++++++++++-
 lib/librte_telemetry/rte_telemetry_internal.h |  25 ++
 mk/rte.app.mk                                 |   2 +-
 4 files changed, 394 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 10311be..a3ab3e9 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>
@@ -18,6 +19,7 @@
 #include "rte_telemetry_internal.h"
 
 #define BUF_SIZE 1024
+#define ACTION_POST 1
 #define SLEEP_TIME 10
 
 static telemetry_impl *static_telemetry;
@@ -39,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;
 }
 
@@ -115,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;
 	}
 
@@ -131,6 +217,38 @@ 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);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	} else if (buffer_read == 0) {
+		goto close_socket;
+	} else {
+		buf[buffer_read] = '\0';
+		ret = rte_telemetry_parse_client_message(telemetry, buf);
+		if (ret < 0)
+			TELEMETRY_LOG_WARN("Parse message failed");
+		goto close_socket;
+	}
+
+close_socket:
+	if (close(telemetry->accept_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		free(telemetry);
+		return -EPERM;
+	}
+	telemetry->accept_fd = 0;
+
+	return 0;
+}
+
+static int32_t
 rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
 {
 	int ret;
@@ -141,8 +259,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);
@@ -151,6 +269,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;
@@ -173,6 +315,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;
 }
 
@@ -292,6 +440,7 @@ rte_telemetry_init()
 			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
 		return -EPERM;
 	}
+	TAILQ_INIT(&static_telemetry->client_list_head);
 
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -308,11 +457,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) {
@@ -329,6 +506,192 @@ 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;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+		return -ENODEV;
+	}
+
+	if (client_path == NULL) {
+		TELEMETRY_LOG_ERR("Invalid client path");
+		return -EINVAL;
+	}
+
+	telemetry_client *client;
+	TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
+		if (strcmp(client_path, client->file_path) == 0) {
+			TELEMETRY_LOG_WARN("'%s' already registered",
+					client_path);
+			return -EINVAL;
+		}
+	}
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd == -1) {
+		TELEMETRY_LOG_ERR("Client socket error");
+		return -EACCES;
+	}
+
+	ret = rte_telemetry_set_socket_nonblock(fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		return -EPERM;
+	}
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	telemetry_client *new_client = malloc(sizeof(telemetry_client));
+	new_client->file_path = strdup(client_path);
+	new_client->fd = fd;
+
+	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
+		TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
+				client_path);
+		ret = rte_telemetry_client_cleanup(new_client);
+		if (ret < 0) {
+			TELEMETRY_LOG_ERR("Client cleanup failed");
+			return -EPERM;
+		}
+		return -EINVAL;
+	}
+
+	TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
+{
+	int ret, action_int;
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+
+	if (root == NULL) {
+		TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+				error.text);
+		goto fail;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+		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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 06/13] telemetry: add parser for client socket messages
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (4 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 07/13] telemetry: update metrics before sending stats Kevin Laatz
                             ` (7 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 a3ab3e9..76d92e9 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
@@ -283,6 +284,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;
@@ -291,8 +293,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 07/13] telemetry: update metrics before sending stats
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (5 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 06/13] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 08/13] telemetry: format json response when " Kevin Laatz
                             ` (6 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 76d92e9..cd97a6e 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 08/13] telemetry: format json response when sending stats
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (6 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 07/13] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 09/13] telemetry: add tests for telemetry api Kevin Laatz
                             ` (5 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 cd97a6e..f5c1bad 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 09/13] telemetry: add tests for telemetry api
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (7 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 08/13] telemetry: format json response when " Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 10/13] telemetry: add ability to disable selftest Kevin Laatz
                             ` (4 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 f5c1bad..717416e 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;
 }
@@ -1140,6 +1172,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_SEQPACKET, 0);
+	struct sockaddr_un addr = {0};
+
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failure");
+		return -1;
+	}
+
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path));
+	unlink(valid_client_path);
+
+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Test socket binding failure");
+		return -1;
+	}
+
+	if (listen(sockfd, 1) < 0) {
+		TELEMETRY_LOG_ERR("Listen failure");
+		return -1;
+	}
+
+	return sockfd;
+}
+
+int32_t
+rte_telemetry_selftest(void)
+{
+	const char *invalid_client_path = SELFTEST_INVALID_CLIENT;
+	const char *valid_client_path = SELFTEST_VALID_CLIENT;
+	int ret, sockfd;
+
+	TELEMETRY_LOG_INFO("Selftest");
+
+	ret = rte_telemetry_init();
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Valid initialisation test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Valid initialisation test passed");
+
+	ret = rte_telemetry_init();
+	if (ret != -EALREADY) {
+		TELEMETRY_LOG_ERR("Invalid initialisation test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid initialisation test passed");
+
+	ret = rte_telemetry_unregister_client(static_telemetry,
+			invalid_client_path);
+	if (ret != -EPERM) {
+		TELEMETRY_LOG_ERR("Invalid unregister test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid unregister test passed");
+
+	sockfd = rte_telemetry_dummy_client_socket(valid_client_path);
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failed");
+		return -1;
+	}
+
+	ret = rte_telemetry_register_client(static_telemetry, valid_client_path);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Valid register test failed: %i", ret);
+		return -1;
+	}
+
+	accept(sockfd, NULL, NULL);
+	TELEMETRY_LOG_INFO("Success - Valid register test passed");
+
+	ret = rte_telemetry_register_client(static_telemetry, valid_client_path);
+	if (ret != -EINVAL) {
+		TELEMETRY_LOG_ERR("Invalid register test failed: %i", ret);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid register test passed");
+
+	ret = rte_telemetry_unregister_client(static_telemetry,
+		invalid_client_path);
+	if (ret != -1) {
+		TELEMETRY_LOG_ERR("Invalid unregister test failed: %i", ret);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid unregister test passed");
+
+	ret = rte_telemetry_unregister_client(static_telemetry, valid_client_path);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Valid unregister test failed: %i", ret);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Valid unregister test passed");
+
+	ret = rte_telemetry_cleanup();
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Cleanup test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Valid cleanup test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_socket_messaging_testing(int index, int socket)
+{
+	struct telemetry_impl *telemetry = calloc(1, sizeof(telemetry_impl));
+	int fd, bad_send_fd, send_fd, bad_fd, bad_recv_fd, recv_fd, ret;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize Telemetry API");
+		return -1;
+	}
+
+	telemetry->server_fd = socket;
+	telemetry->reg_index = index;
+	TELEMETRY_LOG_INFO("Beginning Telemetry socket message Selftest");
+	rte_telemetry_socket_test_setup(telemetry, &send_fd, &recv_fd);
+	TELEMETRY_LOG_INFO("Register valid client test");
+
+	ret = rte_telemetry_socket_register_test(telemetry, &fd, send_fd,
+		recv_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Register valid client test failed!");
+		free(telemetry);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Register valid client test passed!");
+
+	TELEMETRY_LOG_INFO("Register invalid/same client test");
+	ret = rte_telemetry_socket_test_setup(telemetry, &bad_send_fd,
+		&bad_recv_fd);
+	ret = rte_telemetry_socket_register_test(telemetry, &bad_fd,
+		bad_send_fd, bad_recv_fd);
+	if (!ret) {
+		TELEMETRY_LOG_ERR("Register invalid/same client test failed!");
+		free(telemetry);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Register invalid/same client test passed!");
+
+	ret = rte_telemetry_json_socket_message_test(telemetry, fd);
+	if (ret < 0) {
+		free(telemetry);
+		return -1;
+	}
+
+	free(telemetry);
+	return 0;
+}
+
+int32_t
+rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
+	int send_fd, int recv_fd)
+{
+	int ret;
+	char good_req_string[BUF_SIZE];
+
+	snprintf(good_req_string, sizeof(good_req_string),
+	"{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\""
+		":\"%s\"}}", SOCKET_TEST_CLIENT_PATH);
+
+	listen(recv_fd, 1);
+
+	ret = send(send_fd, good_req_string, strlen(good_req_string), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+
+	if (telemetry->register_fail_count != 0)
+		return -1;
+
+	*fd = accept(recv_fd, NULL, NULL);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
+	int *recv_fd)
+{
+	int ret;
+	const char *client_path = SOCKET_TEST_CLIENT_PATH;
+	char socket_path[BUF_SIZE];
+	struct sockaddr_un addr = {0};
+	struct sockaddr_un addrs = {0};
+	*send_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	*recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+
+	listen(telemetry->server_fd, 5);
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+
+	ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not connect socket");
+		return -1;
+	}
+
+	telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	unlink(client_path);
+
+	ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not bind socket");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int32_t
+rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct)
+{
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+	int arraylen, i;
+	json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj,
+	       *statsArrayObj;
+
+	stats = NULL;
+	port = NULL;
+	name = NULL;
+
+	if (buf == NULL) {
+		TELEMETRY_LOG_ERR("JSON message is NULL");
+		return -EINVAL;
+	}
+
+	if (root == NULL) {
+		TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s",
+				error.text);
+		return -EPERM;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_ERR("JSON Request is not a JSON object");
+		json_decref(root);
+		return -EINVAL;
+	}
+
+	status = json_object_get(root, "status_code");
+	if (!status) {
+		TELEMETRY_LOG_ERR("Request does not have status field");
+		return -EINVAL;
+	} else if (!json_is_string(status)) {
+		TELEMETRY_LOG_ERR("Status value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->status_code = strdup(json_string_value(status));
+
+	dataArray = json_object_get(root, "data");
+	if (dataArray == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have data field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(dataArray);
+	if (arraylen == 0) {
+		json_data_struct->data = "null";
+		return -EINVAL;
+	}
+
+	for (i = 0; i < arraylen; i++) {
+		dataArrayObj = json_array_get(dataArray, i);
+		port = json_object_get(dataArrayObj, "port");
+		stats = json_object_get(dataArrayObj, "stats");
+	}
+
+	if (port == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have port field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(port)) {
+		TELEMETRY_LOG_ERR("Port value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->port = json_integer_value(port);
+
+	if (stats == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have stats field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(stats);
+	for (i = 0; i < arraylen; i++) {
+		statsArrayObj = json_array_get(stats, i);
+		name = json_object_get(statsArrayObj, "name");
+		value = json_object_get(statsArrayObj, "value");
+	}
+
+	if (name == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have name field");
+		return -EINVAL;
+	}
+
+	if (!json_is_string(name)) {
+		TELEMETRY_LOG_ERR("Stat name value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_name = strdup(json_string_value(name));
+
+	if (value == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have value field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(value)) {
+		TELEMETRY_LOG_ERR("Stat value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_value = json_integer_value(value);
+
+	return 0;
+}
+
+static void
+rte_telemetry_free_test_data(struct json_data *data)
+{
+	free(data->status_code);
+	free(data->stat_name);
+	free(data);
+}
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	int port = 0;
+	int value = 0;
+	int fail_count = 0;
+	int buffer_read = 0;
+	char buf[BUF_SIZE];
+	struct json_data *data_struct;
+	errno = 0;
+	const char *status = "Status OK: 200";
+	const char *name = "rx_good_packets";
+	const char *valid_json_message = "{\"action\":0,\"command\":"
+	"\"ports_stats_values_by_name\",\"data\":{\"ports\""
+	":[0],\"stats\":[\"rx_good_packets\"]}}";
+
+	ret = send(fd, valid_json_message, strlen(valid_json_message), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not parse stats");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->port != port) {
+		TELEMETRY_LOG_ERR("Port is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->stat_name, name) != 0) {
+		TELEMETRY_LOG_ERR("Stat name is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->stat_value != value) {
+		TELEMETRY_LOG_ERR("Stat value is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	const char *invalid_json = "{]";
+	const char *status = "Status Error: Unknown";
+	const char *data = "null";
+	struct json_data *data_struct;
+	int buffer_read = 0;
+	errno = 0;
+
+	ret = send(fd, invalid_json, strlen(invalid_json), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0)
+		TELEMETRY_LOG_ERR("Could not parse stats");
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->data, data) != 0) {
+		TELEMETRY_LOG_ERR("Data status is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	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..5fe93fa
--- /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_SEQPACKET, 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 10/13] telemetry: add ability to disable selftest
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (8 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 09/13] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 11/13] doc: add telemetry documentation Kevin Laatz
                             ` (3 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 717416e..c43b657 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 11/13] doc: add telemetry documentation
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (9 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 10/13] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 12:25             ` Kovacevic, Marko
  2018-10-22 11:00           ` [PATCH v6 12/13] usertools: add client python script for telemetry Kevin Laatz
                             ` (2 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 <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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 12/13] usertools: add client python script for telemetry
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (10 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-22 11:00           ` [PATCH v6 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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..6dcf62b
--- /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_SEQPACKET)
+        self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+        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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v6 13/13] build: add dependency on telemetry to apps in meson
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (11 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 12/13] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-22 11:00           ` Kevin Laatz
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-22 11:00 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v6 11/13] doc: add telemetry documentation
  2018-10-22 11:00           ` [PATCH v6 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-22 12:25             ` Kovacevic, Marko
  0 siblings, 0 replies; 220+ messages in thread
From: Kovacevic, Marko @ 2018-10-22 12:25 UTC (permalink / raw)
  To: Laatz, Kevin, dev
  Cc: Van Haaren, Harry, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, Richardson, Bruce, Ciara Power, Brian Archbold,
	Laatz, Kevin



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Kevin Laatz
> Sent: Monday, October 22, 2018 12:00 PM
> To: dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com;
> shreyansh.jain@nxp.com; thomas@monjalon.net;
> mattias.ronnblom@ericsson.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>; Laatz, Kevin <kevin.laatz@intel.com>
> Subject: [dpdk-dev] [PATCH v6 11/13] doc: add telemetry documentation
> 
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
> ---
>  MAINTAINERS                    |  5 +++
>  doc/guides/howto/index.rst     |  1 +
>  doc/guides/howto/telemetry.rst | 85
> ++++++++++++++++++++++++++++++++++++++++++

Acked-by: Marko Kovacevic <marko.kovacevic@intel.com>

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v6 04/13] telemetry: add initial connection socket
  2018-10-22 11:00           ` [PATCH v6 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-22 13:50             ` Mattias Rönnblom
  0 siblings, 0 replies; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-22 13:50 UTC (permalink / raw)
  To: Kevin Laatz, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson, Ciara Power, Brian Archbold

On 2018-10-22 13:00, Kevin Laatz wrote:
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
> ---
>   lib/librte_telemetry/rte_telemetry.c          | 226 ++++++++++++++++++++++++++
>   lib/librte_telemetry/rte_telemetry_internal.h |   4 +
>   2 files changed, 230 insertions(+)
> 
> diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
> index ee9c90f..10311be 100644
> --- a/lib/librte_telemetry/rte_telemetry.c
> +++ b/lib/librte_telemetry/rte_telemetry.c
> @@ -3,23 +3,163 @@
>    */
>   
>   #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_option.h>
> +#include <rte_string_fns.h>
>   
>   #include "rte_telemetry.h"
>   #include "rte_telemetry_internal.h"
>   
> +#define BUF_SIZE 1024
>   #define SLEEP_TIME 10
>   
>   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) {

Is this intentional? If so, what's the meaning of "accept_fd == 0", as 
oppose to when accept_fd is -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 +167,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 +196,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;
> +	char socket_path[BUF_SIZE];
> +
> +	if (telemetry == NULL)
> +		return -1;
> +
> +	telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
> +	if (telemetry->server_fd == -1) {
> +		TELEMETRY_LOG_ERR("Failed to open socket");
> +		return -1;
> +	}
> +
> +	ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
> +	if (ret < 0) {
> +		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
> +		goto close_socket;
> +	}
> +
> +	addr.sun_family = AF_UNIX;
> +	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
> +	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
> +	unlink(socket_path);
> +
> +	if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
> +		sizeof(addr)) < 0) {
> +		TELEMETRY_LOG_ERR("Socket binding error");
> +		goto close_socket;
> +	}
> +
> +	return 0;
> +
> +close_socket:
> +	if (close(telemetry->server_fd) < 0) {
> +		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
> +		free(telemetry);

This free() needs to go away, or use-after-free and a double-free() will 
occur in rte_telemetry_init().

> +		return -EPERM;
> +	}
> +
> +	return -1;
> +}
> +
>   int32_t
>   rte_telemetry_init()
>   {
> @@ -77,6 +285,14 @@ rte_telemetry_init()
>   		return -EPERM;
>   	}
>   
> +	ret = rte_telemetry_create_socket(static_telemetry);
> +	if (ret < 0) {
> +		ret = rte_telemetry_cleanup();
> +		if (ret < 0)
> +			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
> +		return -EPERM;
> +	}
> +
>   	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
>   		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
>   		(void *)static_telemetry);
> @@ -95,11 +311,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
> 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v6 05/13] telemetry: add client feature and sockets
  2018-10-22 11:00           ` [PATCH v6 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-22 14:05             ` Mattias Rönnblom
  2018-10-23  8:42               ` Laatz, Kevin
  0 siblings, 1 reply; 220+ messages in thread
From: Mattias Rönnblom @ 2018-10-22 14:05 UTC (permalink / raw)
  To: Kevin Laatz, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson, Ciara Power, Brian Archbold

On 2018-10-22 13:00, Kevin Laatz wrote:
> From: Ciara Power <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 <ciara.power@intel.com>
> Signed-off-by: Brian Archbold <brian.archbold@intel.com>
> Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
> Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
> ---
>   lib/librte_telemetry/meson.build              |   2 +
>   lib/librte_telemetry/rte_telemetry.c          | 369 +++++++++++++++++++++++++-
>   lib/librte_telemetry/rte_telemetry_internal.h |  25 ++
>   mk/rte.app.mk                                 |   2 +-
>   4 files changed, 394 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 10311be..a3ab3e9 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>
> @@ -18,6 +19,7 @@
>   #include "rte_telemetry_internal.h"
>   
>   #define BUF_SIZE 1024
> +#define ACTION_POST 1
>   #define SLEEP_TIME 10
>   
>   static telemetry_impl *static_telemetry;
> @@ -39,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;
>   }
>   
> @@ -115,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;
>   	}
>   
> @@ -131,6 +217,38 @@ 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;

No need to set it to zero.

> +
> +	buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
> +
> +	if (buffer_read == -1) {
> +		TELEMETRY_LOG_ERR("Read error");
> +		return -1;
> +	} else if (buffer_read == 0) {
> +		goto close_socket;
> +	} else {

I would have moved the 'ret' variable to this scope.

> +		buf[buffer_read] = '\0';
> +		ret = rte_telemetry_parse_client_message(telemetry, buf);
> +		if (ret < 0)
> +			TELEMETRY_LOG_WARN("Parse message failed");
> +		goto close_socket;
> +	}
> +
> +close_socket:
> +	if (close(telemetry->accept_fd) < 0) {
> +		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
> +		free(telemetry);
> +		return -EPERM;
> +	}
> +	telemetry->accept_fd = 0;
> +
> +	return 0;
> +}
> +
> +static int32_t
>   rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
>   {
>   	int ret;
> @@ -141,8 +259,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);
> @@ -151,6 +269,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';

read() might return -1, and you'll be writing out-of-bounds.

> +
> +		if (bytes > 0)
> +			telemetry->request_client = client;
>   	}
>   
>   	return 0;
> @@ -173,6 +315,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;
>   }
>   
> @@ -292,6 +440,7 @@ rte_telemetry_init()
>   			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
>   		return -EPERM;
>   	}
> +	TAILQ_INIT(&static_telemetry->client_list_head);
>   
>   	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
>   		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
> @@ -308,11 +457,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) {
> @@ -329,6 +506,192 @@ 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;
> +
> +	if (telemetry == NULL) {
> +		TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
> +		return -ENODEV;
> +	}
> +
> +	if (client_path == NULL) {
> +		TELEMETRY_LOG_ERR("Invalid client path");
> +		return -EINVAL;
> +	}
> +
> +	telemetry_client *client;
> +	TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
> +		if (strcmp(client_path, client->file_path) == 0) {
> +			TELEMETRY_LOG_WARN("'%s' already registered",
> +					client_path);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
> +	if (fd == -1) {
> +		TELEMETRY_LOG_ERR("Client socket error");
> +		return -EACCES;
> +	}
> +
> +	ret = rte_telemetry_set_socket_nonblock(fd);
> +	if (ret < 0) {
> +		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
> +		return -EPERM;
> +	}
> +
> +	addrs.sun_family = AF_UNIX;
> +	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
> +	telemetry_client *new_client = malloc(sizeof(telemetry_client));
> +	new_client->file_path = strdup(client_path);
> +	new_client->fd = fd;
> +
> +	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
> +		TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
> +				client_path);
> +		ret = rte_telemetry_client_cleanup(new_client);
> +		if (ret < 0) {
> +			TELEMETRY_LOG_ERR("Client cleanup failed");
> +			return -EPERM;
> +		}
> +		return -EINVAL;
> +	}
> +
> +	TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
> +
> +	return 0;
> +}
> +
> +int32_t
> +rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
> +{
> +	int ret, action_int;
> +	json_error_t error;
> +	json_t *root = json_loads(buf, 0, &error);
> +
> +	if (root == NULL) {
> +		TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
> +				error.text);
> +		goto fail;
> +	} else if (!json_is_object(root)) {
> +		TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
> +		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;
> +	}
> +

You're leaking the root object here, but maybe that's fixed in 
subsequent patches.

> +	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
> 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v6 05/13] telemetry: add client feature and sockets
  2018-10-22 14:05             ` Mattias Rönnblom
@ 2018-10-23  8:42               ` Laatz, Kevin
  0 siblings, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-23  8:42 UTC (permalink / raw)
  To: Mattias Rönnblom, dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	bruce.richardson, Ciara Power, Brian Archbold

Thanks for the review Mattias.

Will address the comments both here and in the 4/13 review.

Best regards,

Kevin


On 22/10/2018 15:05, Mattias Rönnblom wrote:
> On 2018-10-22 13:00, Kevin Laatz wrote:
>>
>>   @@ -131,6 +217,38 @@ 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;
>
> No need to set it to zero.
>
>> +
>> +    buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
>> +
>> +    if (buffer_read == -1) {
>> +        TELEMETRY_LOG_ERR("Read error");
>> +        return -1;
>> +    } else if (buffer_read == 0) {
>> +        goto close_socket;
>> +    } else {
>
> I would have moved the 'ret' variable to this scope.
>
>>
>> +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';
>
> read() might return -1, and you'll be writing out-of-bounds.
>
>>
>> +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;
>> +    }
>> +
>
> You're leaking the root object here, but maybe that's fixed in 
> subsequent patches.
>
>> +    return 0;
>> +
>> +fail:
>> +    TELEMETRY_LOG_WARN("Client attempted to register with invalid 
>> message");
>> +    return -1;
>> +}
>> +

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v7 00/13] introduce telemetry library
  2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
                             ` (12 preceding siblings ...)
  2018-10-22 11:00           ` [PATCH v6 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-24 13:27           ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 01/13] eal: add option register infrastructure Kevin Laatz
                               ` (14 more replies)
  13 siblings, 15 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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

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

v5:
  - Moved the BUF_SIZE define to fix build (Harry)
  - Set default config for telemetry to 'n' (Harry)
  - Improved Doxygen comments (Thomas)
  - Cleaned up rte_param struct (Thomas)

v6:
  - Renamed rte_param to rte_option (Thomas)
  - Moved internal functions to eal_private.h (Gaetan)
  - Added fail check for pthread_attr_init() (Mattias)
  - Changed socket implementation to SOCK_SEQPACKET (Mattias)
  - Added check to avoid option duplicates (Gaetan)
  - Removed telemetry example from Doxygen comment (Gaetan)
  - General Doxygen clean-up (Thomas)
  - General code clean-up (Mattias)

v7:
  - Fix object leak (Mattias)
  - General clean-up (Mattias)
  - Fix rte_option header define
  - Added release note update
  - Rebased

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 option 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 +
 doc/guides/rel_notes/release_18_11.rst            |    6 +
 lib/Makefile                                      |    2 +
 lib/librte_eal/bsdapp/eal/Makefile                |    1 +
 lib/librte_eal/bsdapp/eal/eal.c                   |   16 +-
 lib/librte_eal/common/Makefile                    |    1 +
 lib/librte_eal/common/eal_filesystem.h            |   15 +-
 lib/librte_eal/common/eal_private.h               |   21 +
 lib/librte_eal/common/include/rte_eal.h           |    9 +
 lib/librte_eal/common/include/rte_option.h        |   63 +
 lib/librte_eal/common/meson.build                 |    2 +
 lib/librte_eal/common/rte_option.c                |   54 +
 lib/librte_eal/linuxapp/eal/Makefile              |    1 +
 lib/librte_eal/linuxapp/eal/eal.c                 |   16 +-
 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              | 1815 +++++++++++++++++++++
 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 ++
 40 files changed, 3622 insertions(+), 19 deletions(-)
 create mode 100644 doc/guides/howto/telemetry.rst
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v7 01/13] eal: add option register infrastructure
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 14:01               ` Gaëtan Rivet
  2018-10-24 13:27             ` [PATCH v7 02/13] eal: make get runtime dir function public Kevin Laatz
                               ` (13 subsequent siblings)
  14 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_eal/bsdapp/eal/Makefile         |  1 +
 lib/librte_eal/bsdapp/eal/eal.c            | 14 ++++++-
 lib/librte_eal/common/Makefile             |  1 +
 lib/librte_eal/common/eal_private.h        | 21 ++++++++++
 lib/librte_eal/common/include/rte_option.h | 63 ++++++++++++++++++++++++++++++
 lib/librte_eal/common/meson.build          |  2 +
 lib/librte_eal/common/rte_option.c         | 54 +++++++++++++++++++++++++
 lib/librte_eal/linuxapp/eal/Makefile       |  1 +
 lib/librte_eal/linuxapp/eal/eal.c          | 14 ++++++-
 lib/librte_eal/rte_eal_version.map         |  1 +
 10 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d19f53c..bfeddaa 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -67,6 +67,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_option.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 7735194..178cfd6 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -791,6 +800,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca6882..87d8c45 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_option.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/eal_private.h b/lib/librte_eal/common/eal_private.h
index b189d67..442c6dc 100644
--- a/lib/librte_eal/common/eal_private.h
+++ b/lib/librte_eal/common/eal_private.h
@@ -349,4 +349,25 @@ dev_sigbus_handler_register(void);
 int
 dev_sigbus_handler_unregister(void);
 
+/**
+ * Check if the option is registered.
+ *
+ * @param option
+ *  The option to be parsed.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -1 on fail
+ */
+int
+rte_option_parse(const char *opt);
+
+/**
+ * Iterate through the registered options and execute the associated
+ * callback if enabled.
+ */
+void
+rte_option_init(void);
+
 #endif /* _EAL_PRIVATE_H_ */
diff --git a/lib/librte_eal/common/include/rte_option.h b/lib/librte_eal/common/include/rte_option.h
new file mode 100644
index 0000000..8957b97
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_option.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_OPTION_H__
+#define __INCLUDE_RTE_OPTION_H__
+
+/**
+ * @file
+ *
+ * This API offers the ability to register options to the EAL command line and
+ * map those options to functions that will be executed at the end of EAL
+ * initialization. These options will be available as part of the EAL command
+ * line of applications and are dynamically managed.
+ *
+ * This is used primarily by DPDK libraries offering command line options.
+ * Currently, this API is limited to registering options without argument.
+ *
+ * The register API can be used to resolve circular dependency issues
+ * between EAL and the library. The library uses EAL, but is also initialized
+ * by EAL. Hence, EAL depends on the init function of the library. The API
+ * introduced in rte_option allows us to register the library init with EAL
+ * (passing a function pointer) and avoid the circular dependency.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_option_cb)(void);
+
+/*
+ * Structure describing the EAL command line option being registered.
+ */
+struct rte_option {
+	TAILQ_ENTRY(rte_option) next; /**< Next entry in the list. */
+	char *opt_str;             /**< The option name. */
+	rte_option_cb cb;          /**< Function called when option is used. */
+	int enabled;               /**< Set when the option is used. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register an option to the EAL command line.
+ * When recognized, the associated function will be executed at the end of EAL
+ * initialization.
+ *
+ * The associated structure must be available the whole time this option is
+ * registered (i.e. not stack memory).
+ *
+ * @param opt
+ *  Structure describing the option to parse.
+ */
+void __rte_experimental
+rte_option_register(struct rte_option *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 04c4143..2a10d57 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -34,6 +34,7 @@ common_sources = files(
 	'malloc_mp.c',
 	'rte_keepalive.c',
 	'rte_malloc.c',
+	'rte_option.c',
 	'rte_reciprocal.c',
 	'rte_service.c'
 )
@@ -71,6 +72,7 @@ common_headers = files(
 	'include/rte_malloc_heap.h',
 	'include/rte_memory.h',
 	'include/rte_memzone.h',
+	'include/rte_option.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_option.c b/lib/librte_eal/common/rte_option.c
new file mode 100644
index 0000000..02d59a8
--- /dev/null
+++ b/lib/librte_eal/common/rte_option.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_option.h>
+
+#include "eal_private.h"
+
+TAILQ_HEAD(rte_option_list, rte_option);
+
+struct rte_option_list rte_option_list =
+	TAILQ_HEAD_INITIALIZER(rte_option_list);
+
+static struct rte_option *option;
+
+int
+rte_option_parse(const char *opt)
+{
+	/* Check if the option is registered */
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt, option->opt_str) == 0) {
+			option->enabled = 1;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void __rte_experimental
+rte_option_register(struct rte_option *opt)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt->opt_str, option->opt_str) == 0)
+			RTE_LOG(INFO, EAL, "Option %s has already been registered.",
+					opt->opt_str);
+			return;
+	}
+
+	TAILQ_INSERT_HEAD(&rte_option_list, opt, next);
+}
+
+void
+rte_option_init(void)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (option->enabled)
+			option->cb();
+	}
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 7280885..51deb57 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -75,6 +75,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_option.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 a00cebf..70ab032 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -1080,6 +1089,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 08e3bc2..12ace18 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -351,6 +351,7 @@ EXPERIMENTAL {
 	rte_mp_request_sync;
 	rte_mp_request_async;
 	rte_mp_sendmsg;
+	rte_option_register;
 	rte_service_lcore_attr_get;
 	rte_service_lcore_attr_reset_all;
 	rte_service_may_be_active;
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 02/13] eal: make get runtime dir function public
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 01/13] eal: add option register infrastructure Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
                               ` (12 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_eal/bsdapp/eal/eal.c         |  2 +-
 lib/librte_eal/common/eal_filesystem.h  | 15 ++++++++-------
 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, 20 insertions(+), 9 deletions(-)

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 178cfd6..52c1dea 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..b3e8ae5 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,8 @@ 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 +67,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 +79,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 +100,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 3ee897c..150ced7 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -507,6 +507,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 70ab032..b75dd01 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 12ace18..42bd367 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -261,6 +261,7 @@ DPDK_18.08 {
 DPDK_18.11 {
 	global:
 
+	rte_eal_get_runtime_dir;
 	rte_eal_hotplug_add;
 	rte_eal_hotplug_remove;
 	rte_strscpy;
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 03/13] telemetry: initial telemetry infrastructure
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 01/13] eal: add option register infrastructure Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 02/13] eal: make get runtime dir function public Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 04/13] telemetry: add initial connection socket Kevin Laatz
                               ` (11 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 option. 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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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           | 123 +++++++++++++++++++++++++
 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, 241 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 be7365e..d59141b 100644
--- a/config/common_base
+++ b/config/common_base
@@ -753,6 +753,11 @@ CONFIG_RTE_LIBRTE_HASH=y
 CONFIG_RTE_LIBRTE_HASH_DEBUG=n
 
 #
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+
+#
 # 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..ee9c90f
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,123 @@
+/* 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_option.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);
+
+	ret = pthread_attr_init(&attr);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Pthread attribute init failed");
+		return -EPERM;
+	}
+
+	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_option option = {
+	.opt_str = "--telemetry",
+	.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_option_register(&option);
+}
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 24351cc..90a4227 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 3203cf0..a5ce715 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_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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 04/13] telemetry: add initial connection socket
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (2 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 05/13] telemetry: add client feature and sockets Kevin Laatz
                               ` (10 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 ee9c90f..816296b 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,163 @@
  */
 
 #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_option.h>
+#include <rte_string_fns.h>
 
 #include "rte_telemetry.h"
 #include "rte_telemetry_internal.h"
 
+#define BUF_SIZE 1024
 #define SLEEP_TIME 10
 
 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) {
+		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 +167,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 +196,67 @@ 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;
+	char socket_path[BUF_SIZE];
+
+	if (telemetry == NULL)
+		return -1;
+
+	telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (telemetry->server_fd == -1) {
+		TELEMETRY_LOG_ERR("Failed to open socket");
+		return -1;
+	}
+
+	ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		goto close_socket;
+	}
+
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+	unlink(socket_path);
+
+	if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
+		sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Socket binding error");
+		goto close_socket;
+	}
+
+	return 0;
+
+close_socket:
+	if (close(telemetry->server_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		return -EPERM;
+	}
+
+	return -1;
+}
+
 int32_t
 rte_telemetry_init()
 {
@@ -77,6 +284,14 @@ rte_telemetry_init()
 		return -EPERM;
 	}
 
+	ret = rte_telemetry_create_socket(static_telemetry);
+	if (ret < 0) {
+		ret = rte_telemetry_cleanup();
+		if (ret < 0)
+			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
+		return -EPERM;
+	}
+
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
 		(void *)static_telemetry);
@@ -95,11 +310,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 05/13] telemetry: add client feature and sockets
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (3 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 06/13] telemetry: add parser for client socket messages Kevin Laatz
                               ` (9 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 816296b..15292de 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>
@@ -18,6 +19,7 @@
 #include "rte_telemetry_internal.h"
 
 #define BUF_SIZE 1024
+#define ACTION_POST 1
 #define SLEEP_TIME 10
 
 static telemetry_impl *static_telemetry;
@@ -39,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;
 }
 
@@ -115,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;
 	}
 
@@ -131,6 +217,38 @@ 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;
+
+	buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	} else if (buffer_read == 0) {
+		goto close_socket;
+	} else {
+		buf[buffer_read] = '\0';
+		ret = rte_telemetry_parse_client_message(telemetry, buf);
+		if (ret < 0)
+			TELEMETRY_LOG_WARN("Parse message failed");
+		goto close_socket;
+	}
+
+close_socket:
+	if (close(telemetry->accept_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		free(telemetry);
+		return -EPERM;
+	}
+	telemetry->accept_fd = 0;
+
+	return 0;
+}
+
+static int32_t
 rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
 {
 	int ret;
@@ -141,8 +259,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);
@@ -151,6 +269,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);
+
+		if (bytes > 0) {
+			client_buf[bytes] = '\0';
+			telemetry->request_client = client;
+		}
 	}
 
 	return 0;
@@ -173,6 +316,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;
 }
 
@@ -291,6 +440,7 @@ rte_telemetry_init()
 			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
 		return -EPERM;
 	}
+	TAILQ_INIT(&static_telemetry->client_list_head);
 
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -307,11 +457,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) {
@@ -328,6 +506,192 @@ 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;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+		return -ENODEV;
+	}
+
+	if (client_path == NULL) {
+		TELEMETRY_LOG_ERR("Invalid client path");
+		return -EINVAL;
+	}
+
+	telemetry_client *client;
+	TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
+		if (strcmp(client_path, client->file_path) == 0) {
+			TELEMETRY_LOG_WARN("'%s' already registered",
+					client_path);
+			return -EINVAL;
+		}
+	}
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd == -1) {
+		TELEMETRY_LOG_ERR("Client socket error");
+		return -EACCES;
+	}
+
+	ret = rte_telemetry_set_socket_nonblock(fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		return -EPERM;
+	}
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	telemetry_client *new_client = malloc(sizeof(telemetry_client));
+	new_client->file_path = strdup(client_path);
+	new_client->fd = fd;
+
+	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
+		TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
+				client_path);
+		ret = rte_telemetry_client_cleanup(new_client);
+		if (ret < 0) {
+			TELEMETRY_LOG_ERR("Client cleanup failed");
+			return -EPERM;
+		}
+		return -EINVAL;
+	}
+
+	TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
+{
+	int ret, action_int;
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+
+	if (root == NULL) {
+		TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+				error.text);
+		goto fail;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+		goto fail;
+	}
+
+	json_t *action = json_object_get(root, "action");
+	if (action == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have action field");
+		goto fail;
+	} else if (!json_is_integer(action)) {
+		TELEMETRY_LOG_WARN("Action value is not an integer");
+		goto fail;
+	}
+
+	json_t *command = json_object_get(root, "command");
+	if (command == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have command field");
+		goto fail;
+	} else if (!json_is_string(command)) {
+		TELEMETRY_LOG_WARN("Command value is not a string");
+		goto fail;
+	}
+
+	action_int = json_integer_value(action);
+	if (action_int != ACTION_POST) {
+		TELEMETRY_LOG_WARN("Invalid action code");
+		goto fail;
+	}
+
+	if (strcmp(json_string_value(command), "clients") != 0) {
+		TELEMETRY_LOG_WARN("Invalid command");
+		goto fail;
+	}
+
+	json_t *data = json_object_get(root, "data");
+	if (data == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have data field");
+		goto fail;
+	}
+
+	json_t *client_path = json_object_get(data, "client_path");
+	if (client_path == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have client_path field");
+		goto fail;
+	}
+
+	if (!json_is_string(client_path)) {
+		TELEMETRY_LOG_WARN("Client_path value is not a string");
+		goto fail;
+	}
+
+	ret = rte_telemetry_register_client(telemetry,
+			json_string_value(client_path));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not register client");
+		telemetry->register_fail_count++;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	TELEMETRY_LOG_WARN("Client attempted to register with invalid message");
+	json_decref(root);
+	return -1;
+}
+
 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 a5ce715..620059c 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_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += -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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 06/13] telemetry: add parser for client socket messages
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (4 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 07/13] telemetry: update metrics before sending stats Kevin Laatz
                               ` (8 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 15292de..d9b67d4 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
@@ -283,6 +284,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,6 +295,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
 		if (bytes > 0) {
 			client_buf[bytes] = '\0';
 			telemetry->request_client = client;
+			ret = rte_telemetry_parse(telemetry, client_buf);
+			if (ret < 0) {
+				TELEMETRY_LOG_WARN("Parse socket input failed: %i",
+						ret);
+				return -1;
+			}
 		}
 	}
 
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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 07/13] telemetry: update metrics before sending stats
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (5 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 06/13] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 08/13] telemetry: format json response when " Kevin Laatz
                               ` (7 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 d9b67d4..556e53d 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 08/13] telemetry: format json response when sending stats
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (6 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 07/13] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 09/13] telemetry: add tests for telemetry api Kevin Laatz
                               ` (6 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 556e53d..3e76108 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 09/13] telemetry: add tests for telemetry api
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (7 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 08/13] telemetry: format json response when " Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 10/13] telemetry: add ability to disable selftest Kevin Laatz
                               ` (5 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 3e76108..324185f 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;
 }
@@ -1139,6 +1171,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_SEQPACKET, 0);
+	struct sockaddr_un addr = {0};
+
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failure");
+		return -1;
+	}
+
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path));
+	unlink(valid_client_path);
+
+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Test socket binding failure");
+		return -1;
+	}
+
+	if (listen(sockfd, 1) < 0) {
+		TELEMETRY_LOG_ERR("Listen failure");
+		return -1;
+	}
+
+	return sockfd;
+}
+
+int32_t
+rte_telemetry_selftest(void)
+{
+	const char *invalid_client_path = SELFTEST_INVALID_CLIENT;
+	const char *valid_client_path = SELFTEST_VALID_CLIENT;
+	int ret, sockfd;
+
+	TELEMETRY_LOG_INFO("Selftest");
+
+	ret = rte_telemetry_init();
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Valid initialisation test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Valid initialisation test passed");
+
+	ret = rte_telemetry_init();
+	if (ret != -EALREADY) {
+		TELEMETRY_LOG_ERR("Invalid initialisation test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid initialisation test passed");
+
+	ret = rte_telemetry_unregister_client(static_telemetry,
+			invalid_client_path);
+	if (ret != -EPERM) {
+		TELEMETRY_LOG_ERR("Invalid unregister test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid unregister test passed");
+
+	sockfd = rte_telemetry_dummy_client_socket(valid_client_path);
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failed");
+		return -1;
+	}
+
+	ret = rte_telemetry_register_client(static_telemetry, valid_client_path);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Valid register test failed: %i", ret);
+		return -1;
+	}
+
+	accept(sockfd, NULL, NULL);
+	TELEMETRY_LOG_INFO("Success - Valid register test passed");
+
+	ret = rte_telemetry_register_client(static_telemetry, valid_client_path);
+	if (ret != -EINVAL) {
+		TELEMETRY_LOG_ERR("Invalid register test failed: %i", ret);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid register test passed");
+
+	ret = rte_telemetry_unregister_client(static_telemetry,
+		invalid_client_path);
+	if (ret != -1) {
+		TELEMETRY_LOG_ERR("Invalid unregister test failed: %i", ret);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Invalid unregister test passed");
+
+	ret = rte_telemetry_unregister_client(static_telemetry, valid_client_path);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Valid unregister test failed: %i", ret);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Valid unregister test passed");
+
+	ret = rte_telemetry_cleanup();
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Cleanup test failed");
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Valid cleanup test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_socket_messaging_testing(int index, int socket)
+{
+	struct telemetry_impl *telemetry = calloc(1, sizeof(telemetry_impl));
+	int fd, bad_send_fd, send_fd, bad_fd, bad_recv_fd, recv_fd, ret;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize Telemetry API");
+		return -1;
+	}
+
+	telemetry->server_fd = socket;
+	telemetry->reg_index = index;
+	TELEMETRY_LOG_INFO("Beginning Telemetry socket message Selftest");
+	rte_telemetry_socket_test_setup(telemetry, &send_fd, &recv_fd);
+	TELEMETRY_LOG_INFO("Register valid client test");
+
+	ret = rte_telemetry_socket_register_test(telemetry, &fd, send_fd,
+		recv_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Register valid client test failed!");
+		free(telemetry);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Register valid client test passed!");
+
+	TELEMETRY_LOG_INFO("Register invalid/same client test");
+	ret = rte_telemetry_socket_test_setup(telemetry, &bad_send_fd,
+		&bad_recv_fd);
+	ret = rte_telemetry_socket_register_test(telemetry, &bad_fd,
+		bad_send_fd, bad_recv_fd);
+	if (!ret) {
+		TELEMETRY_LOG_ERR("Register invalid/same client test failed!");
+		free(telemetry);
+		return -1;
+	}
+
+	TELEMETRY_LOG_INFO("Success - Register invalid/same client test passed!");
+
+	ret = rte_telemetry_json_socket_message_test(telemetry, fd);
+	if (ret < 0) {
+		free(telemetry);
+		return -1;
+	}
+
+	free(telemetry);
+	return 0;
+}
+
+int32_t
+rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd,
+	int send_fd, int recv_fd)
+{
+	int ret;
+	char good_req_string[BUF_SIZE];
+
+	snprintf(good_req_string, sizeof(good_req_string),
+	"{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\""
+		":\"%s\"}}", SOCKET_TEST_CLIENT_PATH);
+
+	listen(recv_fd, 1);
+
+	ret = send(send_fd, good_req_string, strlen(good_req_string), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+
+	if (telemetry->register_fail_count != 0)
+		return -1;
+
+	*fd = accept(recv_fd, NULL, NULL);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd,
+	int *recv_fd)
+{
+	int ret;
+	const char *client_path = SOCKET_TEST_CLIENT_PATH;
+	char socket_path[BUF_SIZE];
+	struct sockaddr_un addr = {0};
+	struct sockaddr_un addrs = {0};
+	*send_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	*recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+
+	listen(telemetry->server_fd, 5);
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+
+	ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not connect socket");
+		return -1;
+	}
+
+	telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	unlink(client_path);
+
+	ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not bind socket");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int32_t
+rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct)
+{
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+	int arraylen, i;
+	json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj,
+	       *statsArrayObj;
+
+	stats = NULL;
+	port = NULL;
+	name = NULL;
+
+	if (buf == NULL) {
+		TELEMETRY_LOG_ERR("JSON message is NULL");
+		return -EINVAL;
+	}
+
+	if (root == NULL) {
+		TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s",
+				error.text);
+		return -EPERM;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_ERR("JSON Request is not a JSON object");
+		json_decref(root);
+		return -EINVAL;
+	}
+
+	status = json_object_get(root, "status_code");
+	if (!status) {
+		TELEMETRY_LOG_ERR("Request does not have status field");
+		return -EINVAL;
+	} else if (!json_is_string(status)) {
+		TELEMETRY_LOG_ERR("Status value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->status_code = strdup(json_string_value(status));
+
+	dataArray = json_object_get(root, "data");
+	if (dataArray == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have data field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(dataArray);
+	if (arraylen == 0) {
+		json_data_struct->data = "null";
+		return -EINVAL;
+	}
+
+	for (i = 0; i < arraylen; i++) {
+		dataArrayObj = json_array_get(dataArray, i);
+		port = json_object_get(dataArrayObj, "port");
+		stats = json_object_get(dataArrayObj, "stats");
+	}
+
+	if (port == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have port field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(port)) {
+		TELEMETRY_LOG_ERR("Port value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->port = json_integer_value(port);
+
+	if (stats == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have stats field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(stats);
+	for (i = 0; i < arraylen; i++) {
+		statsArrayObj = json_array_get(stats, i);
+		name = json_object_get(statsArrayObj, "name");
+		value = json_object_get(statsArrayObj, "value");
+	}
+
+	if (name == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have name field");
+		return -EINVAL;
+	}
+
+	if (!json_is_string(name)) {
+		TELEMETRY_LOG_ERR("Stat name value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_name = strdup(json_string_value(name));
+
+	if (value == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have value field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(value)) {
+		TELEMETRY_LOG_ERR("Stat value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_value = json_integer_value(value);
+
+	return 0;
+}
+
+static void
+rte_telemetry_free_test_data(struct json_data *data)
+{
+	free(data->status_code);
+	free(data->stat_name);
+	free(data);
+}
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	int port = 0;
+	int value = 0;
+	int fail_count = 0;
+	int buffer_read = 0;
+	char buf[BUF_SIZE];
+	struct json_data *data_struct;
+	errno = 0;
+	const char *status = "Status OK: 200";
+	const char *name = "rx_good_packets";
+	const char *valid_json_message = "{\"action\":0,\"command\":"
+	"\"ports_stats_values_by_name\",\"data\":{\"ports\""
+	":[0],\"stats\":[\"rx_good_packets\"]}}";
+
+	ret = send(fd, valid_json_message, strlen(valid_json_message), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not parse stats");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->port != port) {
+		TELEMETRY_LOG_ERR("Port is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->stat_name, name) != 0) {
+		TELEMETRY_LOG_ERR("Stat name is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->stat_value != value) {
+		TELEMETRY_LOG_ERR("Stat value is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	const char *invalid_json = "{]";
+	const char *status = "Status Error: Unknown";
+	const char *data = "null";
+	struct json_data *data_struct;
+	int buffer_read = 0;
+	errno = 0;
+
+	ret = send(fd, invalid_json, strlen(invalid_json), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0)
+		TELEMETRY_LOG_ERR("Could not parse stats");
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->data, data) != 0) {
+		TELEMETRY_LOG_ERR("Data status is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	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..5fe93fa
--- /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_SEQPACKET, 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 10/13] telemetry: add ability to disable selftest
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (8 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 09/13] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 11/13] doc: add telemetry documentation Kevin Laatz
                               ` (4 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 324185f..a169832 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 11/13] doc: add telemetry documentation
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (9 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 10/13] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 12/13] usertools: add client python script for telemetry Kevin Laatz
                               ` (3 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 and a release notes update for
telemetry.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Marko Kovacevic <marko.kovacevic@intel.com>
---
 MAINTAINERS                            |  5 ++
 doc/guides/howto/index.rst             |  1 +
 doc/guides/howto/telemetry.rst         | 85 ++++++++++++++++++++++++++++++++++
 doc/guides/rel_notes/release_18_11.rst |  6 +++
 4 files changed, 97 insertions(+)
 create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index b220479..7612a5b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1208,6 +1208,11 @@ F: test/bpf/
 F: test/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Telemetry
+M: Kevin Laatz <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.
diff --git a/doc/guides/rel_notes/release_18_11.rst b/doc/guides/rel_notes/release_18_11.rst
index 04f3745..e82c790 100644
--- a/doc/guides/rel_notes/release_18_11.rst
+++ b/doc/guides/rel_notes/release_18_11.rst
@@ -189,6 +189,12 @@ New Features
   the specified port. The port must be stopped before the command call in order
   to reconfigure queues.
 
+* **Added Telemetry API.**
+
+  Added the telemetry API which allows applications to transparently expose
+  their telemetry via a UNIX socket in JSON. The JSON can be consumed by any
+  Service Assurance agent, such as CollectD.
+
 * **Add a new sample for vDPA**
 
   The vdpa sample application creates vhost-user sockets by using the
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 12/13] usertools: add client python script for telemetry
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (10 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 13:27             ` [PATCH v7 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
                               ` (2 subsequent siblings)
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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..6dcf62b
--- /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_SEQPACKET)
+        self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+        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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v7 13/13] build: add dependency on telemetry to apps in meson
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (11 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 12/13] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-24 13:27             ` Kevin Laatz
  2018-10-24 14:13             ` [PATCH v7 00/13] introduce telemetry library Thomas Monjalon
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
  14 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 13:27 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 e68d949..78bc3af 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -29,7 +29,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)
 
@@ -43,7 +43,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 cd66618..6006c60 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -24,7 +24,7 @@ sources = files('cmdline.c',
 	'txonly.c',
 	'util.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 90a4227..ae99ab0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -131,6 +131,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v7 01/13] eal: add option register infrastructure
  2018-10-24 13:27             ` [PATCH v7 01/13] eal: add option register infrastructure Kevin Laatz
@ 2018-10-24 14:01               ` Gaëtan Rivet
  2018-10-24 14:33                 ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Gaëtan Rivet @ 2018-10-24 14:01 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson

Hi Kevin,

On Wed, Oct 24, 2018 at 02:27:13PM +0100, Kevin Laatz wrote:
> 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 <kevin.laatz@intel.com>
> Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

I think this is good enough,

Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>

The only remaining issue is rte_option_init().
Sorry I missed your previous message and did not respond in time, I
would have opted for leaving a return value to at least be able to stop
the init on error. It is possible to force the callback type to return
an error value along with a string / hint describing the error. It
should not be hard to add it later, so not blocking IMO.

-- 
Gaëtan Rivet
6WIND

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v7 00/13] introduce telemetry library
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (12 preceding siblings ...)
  2018-10-24 13:27             ` [PATCH v7 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-24 14:13             ` Thomas Monjalon
  2018-10-24 14:49               ` Laatz, Kevin
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
  14 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-24 14:13 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

24/10/2018 15:27, Kevin Laatz:
> This patchset introduces a Telemetry library for DPDK Service Assurance.
> This library provides an easy way to query DPDK Ethdev metrics.

This new library is not defined as experimental.
Is it on purpose?

We are supposed to give an experimental period to new APIs:
	http://doc.dpdk.org/guides/contributing/versioning.html#general-guidelines

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v7 01/13] eal: add option register infrastructure
  2018-10-24 14:01               ` Gaëtan Rivet
@ 2018-10-24 14:33                 ` Thomas Monjalon
  2018-10-24 14:52                   ` Laatz, Kevin
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-24 14:33 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: Gaëtan Rivet, dev, harry.van.haaren, stephen,
	shreyansh.jain, mattias.ronnblom, bruce.richardson

24/10/2018 16:01, Gaëtan Rivet:
> Hi Kevin,
> 
> On Wed, Oct 24, 2018 at 02:27:13PM +0100, Kevin Laatz wrote:
> > 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 <kevin.laatz@intel.com>
> > Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
> 
> I think this is good enough,
> 
> Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>

Yes it looks good enough.
And it compiles fine in my test.

> The only remaining issue is rte_option_init().
> Sorry I missed your previous message and did not respond in time, I
> would have opted for leaving a return value to at least be able to stop
> the init on error. It is possible to force the callback type to return
> an error value along with a string / hint describing the error. It
> should not be hard to add it later, so not blocking IMO.

I think you need to set this API as experimental.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v7 00/13] introduce telemetry library
  2018-10-24 14:13             ` [PATCH v7 00/13] introduce telemetry library Thomas Monjalon
@ 2018-10-24 14:49               ` Laatz, Kevin
  0 siblings, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-24 14:49 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson

On 24/10/2018 15:13, Thomas Monjalon wrote:
> 24/10/2018 15:27, Kevin Laatz:
>> This patchset introduces a Telemetry library for DPDK Service Assurance.
>> This library provides an easy way to query DPDK Ethdev metrics.
> This new library is not defined as experimental.
> Is it on purpose?
>
> We are supposed to give an experimental period to new APIs:
> 	http://doc.dpdk.org/guides/contributing/versioning.html#general-guidelines
Will do, Thanks.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v7 01/13] eal: add option register infrastructure
  2018-10-24 14:33                 ` Thomas Monjalon
@ 2018-10-24 14:52                   ` Laatz, Kevin
  2018-10-24 15:05                     ` Laatz, Kevin
  0 siblings, 1 reply; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-24 14:52 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Gaëtan Rivet, dev, harry.van.haaren, stephen,
	shreyansh.jain, mattias.ronnblom, bruce.richardson

On 24/10/2018 15:33, Thomas Monjalon wrote:
> 24/10/2018 16:01, Gaëtan Rivet:
>> Hi Kevin,
>>
>> On Wed, Oct 24, 2018 at 02:27:13PM +0100, Kevin Laatz wrote:
>>> 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 <kevin.laatz@intel.com>
>>> Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
>> I think this is good enough,
>>
>> Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>
> Yes it looks good enough.
> And it compiles fine in my test.
>
>> The only remaining issue is rte_option_init().
>> Sorry I missed your previous message and did not respond in time, I
>> would have opted for leaving a return value to at least be able to stop
>> the init on error. It is possible to force the callback type to return
>> an error value along with a string / hint describing the error. It
>> should not be hard to add it later, so not blocking IMO.
Agree this can be added in the future.
> I think you need to set this API as experimental.
Will do, thanks.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v7 01/13] eal: add option register infrastructure
  2018-10-24 14:52                   ` Laatz, Kevin
@ 2018-10-24 15:05                     ` Laatz, Kevin
  0 siblings, 0 replies; 220+ messages in thread
From: Laatz, Kevin @ 2018-10-24 15:05 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Gaëtan Rivet, dev, harry.van.haaren, stephen,
	shreyansh.jain, mattias.ronnblom, bruce.richardson

On 24/10/2018 15:52, Laatz, Kevin wrote:
> On 24/10/2018 15:33, Thomas Monjalon wrote:
>> 24/10/2018 16:01, Gaëtan Rivet:
>>> Hi Kevin,
>>>
>>> On Wed, Oct 24, 2018 at 02:27:13PM +0100, Kevin Laatz wrote:
>>>> 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 <kevin.laatz@intel.com>
>>>> Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
>>> I think this is good enough,
>>>
>>> Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>
>> Yes it looks good enough.
>> And it compiles fine in my test.
>>
>>> The only remaining issue is rte_option_init().
>>> Sorry I missed your previous message and did not respond in time, I
>>> would have opted for leaving a return value to at least be able to stop
>>> the init on error. It is possible to force the callback type to return
>>> an error value along with a string / hint describing the error. It
>>> should not be hard to add it later, so not blocking IMO.
> Agree this can be added in the future.
>> I think you need to set this API as experimental.
> Will do, thanks.
Correction: This is an internal function (eal_private.h), so no need to 
mark as experimental. rte_option_register() is marked as experimental.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v8 00/13] introduce telemetry library
  2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
                               ` (13 preceding siblings ...)
  2018-10-24 14:13             ` [PATCH v7 00/13] introduce telemetry library Thomas Monjalon
@ 2018-10-24 16:02             ` Kevin Laatz
  2018-10-24 16:02               ` [PATCH v8 01/13] eal: add option register infrastructure Kevin Laatz
                                 ` (13 more replies)
  14 siblings, 14 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:02 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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

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

v5:
  - Moved the BUF_SIZE define to fix build (Harry)
  - Set default config for telemetry to 'n' (Harry)
  - Improved Doxygen comments (Thomas)
  - Cleaned up rte_param struct (Thomas)

v6:
  - Renamed rte_param to rte_option (Thomas)
  - Moved internal functions to eal_private.h (Gaetan)
  - Added fail check for pthread_attr_init() (Mattias)
  - Changed socket implementation to SOCK_SEQPACKET (Mattias)
  - Added check to avoid option duplicates (Gaetan)
  - Removed telemetry example from Doxygen comment (Gaetan)
  - General Doxygen clean-up (Thomas)
  - General code clean-up (Mattias)

v7:
  - Fix object leak (Mattias)
  - General clean-up (Mattias)
  - Fix rte_option header define
  - Added release note update
  - Rebased

v8:
  - Added missing experimental tags
  - Added Gaetan's Ack from ML

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 option 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 +
 doc/guides/rel_notes/release_18_11.rst            |    6 +
 lib/Makefile                                      |    2 +
 lib/librte_eal/bsdapp/eal/Makefile                |    1 +
 lib/librte_eal/bsdapp/eal/eal.c                   |   16 +-
 lib/librte_eal/common/Makefile                    |    1 +
 lib/librte_eal/common/eal_filesystem.h            |   15 +-
 lib/librte_eal/common/eal_private.h               |   21 +
 lib/librte_eal/common/include/rte_eal.h           |    9 +
 lib/librte_eal/common/include/rte_option.h        |   63 +
 lib/librte_eal/common/meson.build                 |    2 +
 lib/librte_eal/common/rte_option.c                |   54 +
 lib/librte_eal/linuxapp/eal/Makefile              |    1 +
 lib/librte_eal/linuxapp/eal/eal.c                 |   16 +-
 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              | 1815 +++++++++++++++++++++
 lib/librte_telemetry/rte_telemetry.h              |   57 +
 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    |    9 +
 lib/meson.build                                   |    3 +-
 meson.build                                       |    1 +
 mk/rte.app.mk                                     |    1 +
 usertools/dpdk-telemetry-client.py                |  116 ++
 40 files changed, 3633 insertions(+), 19 deletions(-)
 create mode 100644 doc/guides/howto/telemetry.rst
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.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

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v8 01/13] eal: add option register infrastructure
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
@ 2018-10-24 16:02               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 02/13] eal: make get runtime dir function public Kevin Laatz
                                 ` (12 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:02 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>
---
 lib/librte_eal/bsdapp/eal/Makefile         |  1 +
 lib/librte_eal/bsdapp/eal/eal.c            | 14 ++++++-
 lib/librte_eal/common/Makefile             |  1 +
 lib/librte_eal/common/eal_private.h        | 21 ++++++++++
 lib/librte_eal/common/include/rte_option.h | 63 ++++++++++++++++++++++++++++++
 lib/librte_eal/common/meson.build          |  2 +
 lib/librte_eal/common/rte_option.c         | 54 +++++++++++++++++++++++++
 lib/librte_eal/linuxapp/eal/Makefile       |  1 +
 lib/librte_eal/linuxapp/eal/eal.c          | 14 ++++++-
 lib/librte_eal/rte_eal_version.map         |  1 +
 10 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d19f53c..bfeddaa 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -67,6 +67,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_option.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 7735194..178cfd6 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -791,6 +800,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca6882..87d8c45 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_option.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/eal_private.h b/lib/librte_eal/common/eal_private.h
index b189d67..442c6dc 100644
--- a/lib/librte_eal/common/eal_private.h
+++ b/lib/librte_eal/common/eal_private.h
@@ -349,4 +349,25 @@ dev_sigbus_handler_register(void);
 int
 dev_sigbus_handler_unregister(void);
 
+/**
+ * Check if the option is registered.
+ *
+ * @param option
+ *  The option to be parsed.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -1 on fail
+ */
+int
+rte_option_parse(const char *opt);
+
+/**
+ * Iterate through the registered options and execute the associated
+ * callback if enabled.
+ */
+void
+rte_option_init(void);
+
 #endif /* _EAL_PRIVATE_H_ */
diff --git a/lib/librte_eal/common/include/rte_option.h b/lib/librte_eal/common/include/rte_option.h
new file mode 100644
index 0000000..8957b97
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_option.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_OPTION_H__
+#define __INCLUDE_RTE_OPTION_H__
+
+/**
+ * @file
+ *
+ * This API offers the ability to register options to the EAL command line and
+ * map those options to functions that will be executed at the end of EAL
+ * initialization. These options will be available as part of the EAL command
+ * line of applications and are dynamically managed.
+ *
+ * This is used primarily by DPDK libraries offering command line options.
+ * Currently, this API is limited to registering options without argument.
+ *
+ * The register API can be used to resolve circular dependency issues
+ * between EAL and the library. The library uses EAL, but is also initialized
+ * by EAL. Hence, EAL depends on the init function of the library. The API
+ * introduced in rte_option allows us to register the library init with EAL
+ * (passing a function pointer) and avoid the circular dependency.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_option_cb)(void);
+
+/*
+ * Structure describing the EAL command line option being registered.
+ */
+struct rte_option {
+	TAILQ_ENTRY(rte_option) next; /**< Next entry in the list. */
+	char *opt_str;             /**< The option name. */
+	rte_option_cb cb;          /**< Function called when option is used. */
+	int enabled;               /**< Set when the option is used. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register an option to the EAL command line.
+ * When recognized, the associated function will be executed at the end of EAL
+ * initialization.
+ *
+ * The associated structure must be available the whole time this option is
+ * registered (i.e. not stack memory).
+ *
+ * @param opt
+ *  Structure describing the option to parse.
+ */
+void __rte_experimental
+rte_option_register(struct rte_option *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 04c4143..2a10d57 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -34,6 +34,7 @@ common_sources = files(
 	'malloc_mp.c',
 	'rte_keepalive.c',
 	'rte_malloc.c',
+	'rte_option.c',
 	'rte_reciprocal.c',
 	'rte_service.c'
 )
@@ -71,6 +72,7 @@ common_headers = files(
 	'include/rte_malloc_heap.h',
 	'include/rte_memory.h',
 	'include/rte_memzone.h',
+	'include/rte_option.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_option.c b/lib/librte_eal/common/rte_option.c
new file mode 100644
index 0000000..02d59a8
--- /dev/null
+++ b/lib/librte_eal/common/rte_option.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_option.h>
+
+#include "eal_private.h"
+
+TAILQ_HEAD(rte_option_list, rte_option);
+
+struct rte_option_list rte_option_list =
+	TAILQ_HEAD_INITIALIZER(rte_option_list);
+
+static struct rte_option *option;
+
+int
+rte_option_parse(const char *opt)
+{
+	/* Check if the option is registered */
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt, option->opt_str) == 0) {
+			option->enabled = 1;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void __rte_experimental
+rte_option_register(struct rte_option *opt)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt->opt_str, option->opt_str) == 0)
+			RTE_LOG(INFO, EAL, "Option %s has already been registered.",
+					opt->opt_str);
+			return;
+	}
+
+	TAILQ_INSERT_HEAD(&rte_option_list, opt, next);
+}
+
+void
+rte_option_init(void)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (option->enabled)
+			option->cb();
+	}
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 7280885..51deb57 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -75,6 +75,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_option.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 a00cebf..70ab032 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -1080,6 +1089,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index 08e3bc2..12ace18 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -351,6 +351,7 @@ EXPERIMENTAL {
 	rte_mp_request_sync;
 	rte_mp_request_async;
 	rte_mp_sendmsg;
+	rte_option_register;
 	rte_service_lcore_attr_get;
 	rte_service_lcore_attr_reset_all;
 	rte_service_may_be_active;
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 02/13] eal: make get runtime dir function public
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
  2018-10-24 16:02               ` [PATCH v8 01/13] eal: add option register infrastructure Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
                                 ` (11 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_eal/bsdapp/eal/eal.c         |  2 +-
 lib/librte_eal/common/eal_filesystem.h  | 15 ++++++++-------
 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, 20 insertions(+), 9 deletions(-)

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 178cfd6..52c1dea 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..b3e8ae5 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,8 @@ 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 +67,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 +79,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 +100,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 3ee897c..150ced7 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -507,6 +507,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 70ab032..b75dd01 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 12ace18..42bd367 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -261,6 +261,7 @@ DPDK_18.08 {
 DPDK_18.11 {
 	global:
 
+	rte_eal_get_runtime_dir;
 	rte_eal_hotplug_add;
 	rte_eal_hotplug_remove;
 	rte_strscpy;
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 03/13] telemetry: initial telemetry infrastructure
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
  2018-10-24 16:02               ` [PATCH v8 01/13] eal: add option register infrastructure Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 02/13] eal: make get runtime dir function public Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-25 20:33                 ` Thomas Monjalon
  2018-10-24 16:03               ` [PATCH v8 04/13] telemetry: add initial connection socket Kevin Laatz
                                 ` (10 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 option. 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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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           | 123 +++++++++++++++++++++++++
 lib/librte_telemetry/rte_telemetry.h           |  42 +++++++++
 lib/librte_telemetry/rte_telemetry_internal.h  |  32 +++++++
 lib/librte_telemetry/rte_telemetry_version.map |   8 ++
 lib/meson.build                                |   2 +-
 mk/rte.app.mk                                  |   1 +
 10 files changed, 249 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 be7365e..d59141b 100644
--- a/config/common_base
+++ b/config/common_base
@@ -753,6 +753,11 @@ CONFIG_RTE_LIBRTE_HASH=y
 CONFIG_RTE_LIBRTE_HASH_DEBUG=n
 
 #
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+
+#
 # 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..7f4ad03
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,123 @@
+/* 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_option.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_experimental
+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);
+
+	ret = pthread_attr_init(&attr);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Pthread attribute init failed");
+		return -EPERM;
+	}
+
+	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_experimental
+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_option option = {
+	.opt_str = "--telemetry",
+	.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_option_register(&option);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 0000000..1e8dce6
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 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_experimental
+rte_telemetry_init(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clean up and free memory.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -EPERM on failure
+ */
+int32_t __rte_experimental
+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..bbcd9a7
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,8 @@
+EXPERIMENTAL {
+	global:
+
+	rte_telemetry_cleanup;
+	rte_telemetry_init;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 24351cc..90a4227 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 3203cf0..a5ce715 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_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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 04/13] telemetry: add initial connection socket
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (2 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 05/13] telemetry: add client feature and sockets Kevin Laatz
                                 ` (9 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 7f4ad03..e9b3330 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,163 @@
  */
 
 #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_option.h>
+#include <rte_string_fns.h>
 
 #include "rte_telemetry.h"
 #include "rte_telemetry_internal.h"
 
+#define BUF_SIZE 1024
 #define SLEEP_TIME 10
 
 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) {
+		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 +167,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 +196,67 @@ 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;
+	char socket_path[BUF_SIZE];
+
+	if (telemetry == NULL)
+		return -1;
+
+	telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (telemetry->server_fd == -1) {
+		TELEMETRY_LOG_ERR("Failed to open socket");
+		return -1;
+	}
+
+	ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		goto close_socket;
+	}
+
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+	unlink(socket_path);
+
+	if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
+		sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Socket binding error");
+		goto close_socket;
+	}
+
+	return 0;
+
+close_socket:
+	if (close(telemetry->server_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		return -EPERM;
+	}
+
+	return -1;
+}
+
 int32_t __rte_experimental
 rte_telemetry_init()
 {
@@ -77,6 +284,14 @@ rte_telemetry_init()
 		return -EPERM;
 	}
 
+	ret = rte_telemetry_create_socket(static_telemetry);
+	if (ret < 0) {
+		ret = rte_telemetry_cleanup();
+		if (ret < 0)
+			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
+		return -EPERM;
+	}
+
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
 		(void *)static_telemetry);
@@ -95,11 +310,21 @@ rte_telemetry_init()
 int32_t __rte_experimental
 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (3 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 04/13] telemetry: add initial connection socket Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-25 20:29                 ` Thomas Monjalon
  2018-10-24 16:03               ` [PATCH v8 06/13] telemetry: add parser for client socket messages Kevin Laatz
                                 ` (8 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 e9b3330..3c8b922 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>
@@ -18,6 +19,7 @@
 #include "rte_telemetry_internal.h"
 
 #define BUF_SIZE 1024
+#define ACTION_POST 1
 #define SLEEP_TIME 10
 
 static telemetry_impl *static_telemetry;
@@ -39,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;
 }
 
@@ -115,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;
 	}
 
@@ -131,6 +217,38 @@ 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;
+
+	buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	} else if (buffer_read == 0) {
+		goto close_socket;
+	} else {
+		buf[buffer_read] = '\0';
+		ret = rte_telemetry_parse_client_message(telemetry, buf);
+		if (ret < 0)
+			TELEMETRY_LOG_WARN("Parse message failed");
+		goto close_socket;
+	}
+
+close_socket:
+	if (close(telemetry->accept_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		free(telemetry);
+		return -EPERM;
+	}
+	telemetry->accept_fd = 0;
+
+	return 0;
+}
+
+static int32_t
 rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
 {
 	int ret;
@@ -141,8 +259,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);
@@ -151,6 +269,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);
+
+		if (bytes > 0) {
+			client_buf[bytes] = '\0';
+			telemetry->request_client = client;
+		}
 	}
 
 	return 0;
@@ -173,6 +316,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;
 }
 
@@ -291,6 +440,7 @@ rte_telemetry_init()
 			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
 		return -EPERM;
 	}
+	TAILQ_INIT(&static_telemetry->client_list_head);
 
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -307,11 +457,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_experimental
 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) {
@@ -328,6 +506,192 @@ 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;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+		return -ENODEV;
+	}
+
+	if (client_path == NULL) {
+		TELEMETRY_LOG_ERR("Invalid client path");
+		return -EINVAL;
+	}
+
+	telemetry_client *client;
+	TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
+		if (strcmp(client_path, client->file_path) == 0) {
+			TELEMETRY_LOG_WARN("'%s' already registered",
+					client_path);
+			return -EINVAL;
+		}
+	}
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd == -1) {
+		TELEMETRY_LOG_ERR("Client socket error");
+		return -EACCES;
+	}
+
+	ret = rte_telemetry_set_socket_nonblock(fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		return -EPERM;
+	}
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	telemetry_client *new_client = malloc(sizeof(telemetry_client));
+	new_client->file_path = strdup(client_path);
+	new_client->fd = fd;
+
+	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
+		TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
+				client_path);
+		ret = rte_telemetry_client_cleanup(new_client);
+		if (ret < 0) {
+			TELEMETRY_LOG_ERR("Client cleanup failed");
+			return -EPERM;
+		}
+		return -EINVAL;
+	}
+
+	TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
+{
+	int ret, action_int;
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+
+	if (root == NULL) {
+		TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+				error.text);
+		goto fail;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+		goto fail;
+	}
+
+	json_t *action = json_object_get(root, "action");
+	if (action == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have action field");
+		goto fail;
+	} else if (!json_is_integer(action)) {
+		TELEMETRY_LOG_WARN("Action value is not an integer");
+		goto fail;
+	}
+
+	json_t *command = json_object_get(root, "command");
+	if (command == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have command field");
+		goto fail;
+	} else if (!json_is_string(command)) {
+		TELEMETRY_LOG_WARN("Command value is not a string");
+		goto fail;
+	}
+
+	action_int = json_integer_value(action);
+	if (action_int != ACTION_POST) {
+		TELEMETRY_LOG_WARN("Invalid action code");
+		goto fail;
+	}
+
+	if (strcmp(json_string_value(command), "clients") != 0) {
+		TELEMETRY_LOG_WARN("Invalid command");
+		goto fail;
+	}
+
+	json_t *data = json_object_get(root, "data");
+	if (data == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have data field");
+		goto fail;
+	}
+
+	json_t *client_path = json_object_get(data, "client_path");
+	if (client_path == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have client_path field");
+		goto fail;
+	}
+
+	if (!json_is_string(client_path)) {
+		TELEMETRY_LOG_WARN("Client_path value is not a string");
+		goto fail;
+	}
+
+	ret = rte_telemetry_register_client(telemetry,
+			json_string_value(client_path));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not register client");
+		telemetry->register_fail_count++;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	TELEMETRY_LOG_WARN("Client attempted to register with invalid message");
+	json_decref(root);
+	return -1;
+}
+
 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 a5ce715..620059c 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_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += -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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 06/13] telemetry: add parser for client socket messages
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (4 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 07/13] telemetry: update metrics before sending stats Kevin Laatz
                                 ` (7 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 3c8b922..cac7884 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
@@ -283,6 +284,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,6 +295,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
 		if (bytes > 0) {
 			client_buf[bytes] = '\0';
 			telemetry->request_client = client;
+			ret = rte_telemetry_parse(telemetry, client_buf);
+			if (ret < 0) {
+				TELEMETRY_LOG_WARN("Parse socket input failed: %i",
+						ret);
+				return -1;
+			}
 		}
 	}
 
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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 07/13] telemetry: update metrics before sending stats
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (5 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 06/13] telemetry: add parser for client socket messages Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 08/13] telemetry: format json response when " Kevin Laatz
                                 ` (6 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 cac7884..7616e7c 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 08/13] telemetry: format json response when sending stats
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (6 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 07/13] telemetry: update metrics before sending stats Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 09/13] telemetry: add tests for telemetry api Kevin Laatz
                                 ` (5 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 7616e7c..36cd748 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 09/13] telemetry: add tests for telemetry api
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (7 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 08/13] telemetry: format json response when " Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 10/13] telemetry: add ability to disable selftest Kevin Laatz
                                 ` (4 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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              |  15 +
 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, 1284 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 36cd748..5b66b71 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;
 }
@@ -1139,6 +1171,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_SEQPACKET, 0);
+	struct sockaddr_un addr = {0};
+
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failure");
+		return -1;
+	}
+
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path));
+	unlink(valid_client_path);
+
+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Test socket binding failure");
+		return -1;
+	}
+
+	if (listen(sockfd, 1) < 0) {
+		TELEMETRY_LOG_ERR("Listen failure");
+		return -1;
+	}
+
+	return sockfd;
+}
+
+int32_t __rte_experimental
+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_SEQPACKET, 0);
+	*recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+
+	listen(telemetry->server_fd, 5);
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+
+	ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not connect socket");
+		return -1;
+	}
+
+	telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	unlink(client_path);
+
+	ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not bind socket");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int32_t
+rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct)
+{
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+	int arraylen, i;
+	json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj,
+	       *statsArrayObj;
+
+	stats = NULL;
+	port = NULL;
+	name = NULL;
+
+	if (buf == NULL) {
+		TELEMETRY_LOG_ERR("JSON message is NULL");
+		return -EINVAL;
+	}
+
+	if (root == NULL) {
+		TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s",
+				error.text);
+		return -EPERM;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_ERR("JSON Request is not a JSON object");
+		json_decref(root);
+		return -EINVAL;
+	}
+
+	status = json_object_get(root, "status_code");
+	if (!status) {
+		TELEMETRY_LOG_ERR("Request does not have status field");
+		return -EINVAL;
+	} else if (!json_is_string(status)) {
+		TELEMETRY_LOG_ERR("Status value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->status_code = strdup(json_string_value(status));
+
+	dataArray = json_object_get(root, "data");
+	if (dataArray == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have data field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(dataArray);
+	if (arraylen == 0) {
+		json_data_struct->data = "null";
+		return -EINVAL;
+	}
+
+	for (i = 0; i < arraylen; i++) {
+		dataArrayObj = json_array_get(dataArray, i);
+		port = json_object_get(dataArrayObj, "port");
+		stats = json_object_get(dataArrayObj, "stats");
+	}
+
+	if (port == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have port field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(port)) {
+		TELEMETRY_LOG_ERR("Port value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->port = json_integer_value(port);
+
+	if (stats == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have stats field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(stats);
+	for (i = 0; i < arraylen; i++) {
+		statsArrayObj = json_array_get(stats, i);
+		name = json_object_get(statsArrayObj, "name");
+		value = json_object_get(statsArrayObj, "value");
+	}
+
+	if (name == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have name field");
+		return -EINVAL;
+	}
+
+	if (!json_is_string(name)) {
+		TELEMETRY_LOG_ERR("Stat name value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_name = strdup(json_string_value(name));
+
+	if (value == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have value field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(value)) {
+		TELEMETRY_LOG_ERR("Stat value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_value = json_integer_value(value);
+
+	return 0;
+}
+
+static void
+rte_telemetry_free_test_data(struct json_data *data)
+{
+	free(data->status_code);
+	free(data->stat_name);
+	free(data);
+}
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	int port = 0;
+	int value = 0;
+	int fail_count = 0;
+	int buffer_read = 0;
+	char buf[BUF_SIZE];
+	struct json_data *data_struct;
+	errno = 0;
+	const char *status = "Status OK: 200";
+	const char *name = "rx_good_packets";
+	const char *valid_json_message = "{\"action\":0,\"command\":"
+	"\"ports_stats_values_by_name\",\"data\":{\"ports\""
+	":[0],\"stats\":[\"rx_good_packets\"]}}";
+
+	ret = send(fd, valid_json_message, strlen(valid_json_message), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not parse stats");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->port != port) {
+		TELEMETRY_LOG_ERR("Port is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->stat_name, name) != 0) {
+		TELEMETRY_LOG_ERR("Stat name is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->stat_value != value) {
+		TELEMETRY_LOG_ERR("Stat value is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	const char *invalid_json = "{]";
+	const char *status = "Status Error: Unknown";
+	const char *data = "null";
+	struct json_data *data_struct;
+	int buffer_read = 0;
+	errno = 0;
+
+	ret = send(fd, invalid_json, strlen(invalid_json), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0)
+		TELEMETRY_LOG_ERR("Could not parse stats");
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->data, data) != 0) {
+		TELEMETRY_LOG_ERR("Data status is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	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 1e8dce6..807bd9f 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -39,4 +39,19 @@ rte_telemetry_init(void);
 int32_t __rte_experimental
 rte_telemetry_cleanup(void);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 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_experimental
+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..5fe93fa
--- /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_SEQPACKET, 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 bbcd9a7..b5a8265 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	rte_telemetry_cleanup;
 	rte_telemetry_init;
+	rte_telemetry_selftest;
 
 	local: *;
 };
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 10/13] telemetry: add ability to disable selftest
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (8 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 09/13] telemetry: add tests for telemetry api Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 11/13] doc: add telemetry documentation Kevin Laatz
                                 ` (3 subsequent siblings)
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 5b66b71..2e27491 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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 11/13] doc: add telemetry documentation
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (9 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 10/13] telemetry: add ability to disable selftest Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-25 20:31                 ` Thomas Monjalon
  2018-10-24 16:03               ` [PATCH v8 12/13] usertools: add client python script for telemetry Kevin Laatz
                                 ` (2 subsequent siblings)
  13 siblings, 1 reply; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 and a release notes update for
telemetry.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Marko Kovacevic <marko.kovacevic@intel.com>
---
 MAINTAINERS                            |  5 ++
 doc/guides/howto/index.rst             |  1 +
 doc/guides/howto/telemetry.rst         | 85 ++++++++++++++++++++++++++++++++++
 doc/guides/rel_notes/release_18_11.rst |  6 +++
 4 files changed, 97 insertions(+)
 create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index b220479..6b64c15 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1208,6 +1208,11 @@ F: test/bpf/
 F: test/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Telemetry - EXPERIMENTAL
+M: Kevin Laatz <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.
diff --git a/doc/guides/rel_notes/release_18_11.rst b/doc/guides/rel_notes/release_18_11.rst
index 04f3745..e82c790 100644
--- a/doc/guides/rel_notes/release_18_11.rst
+++ b/doc/guides/rel_notes/release_18_11.rst
@@ -189,6 +189,12 @@ New Features
   the specified port. The port must be stopped before the command call in order
   to reconfigure queues.
 
+* **Added Telemetry API.**
+
+  Added the telemetry API which allows applications to transparently expose
+  their telemetry via a UNIX socket in JSON. The JSON can be consumed by any
+  Service Assurance agent, such as CollectD.
+
 * **Add a new sample for vDPA**
 
   The vdpa sample application creates vhost-user sockets by using the
-- 
2.9.5

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 12/13] usertools: add client python script for telemetry
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (10 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-24 16:03               ` [PATCH v8 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold,
	Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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..6dcf62b
--- /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_SEQPACKET)
+        self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+        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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v8 13/13] build: add dependency on telemetry to apps in meson
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (11 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 12/13] usertools: add client python script for telemetry Kevin Laatz
@ 2018-10-24 16:03               ` Kevin Laatz
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
  13 siblings, 0 replies; 220+ messages in thread
From: Kevin Laatz @ 2018-10-24 16:03 UTC (permalink / raw)
  To: dev
  Cc: harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain, thomas,
	mattias.ronnblom, bruce.richardson, Kevin Laatz

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 e68d949..78bc3af 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -29,7 +29,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)
 
@@ -43,7 +43,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 cd66618..6006c60 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -24,7 +24,7 @@ sources = files('cmdline.c',
 	'txonly.c',
 	'util.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 90a4227..ae99ab0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -131,6 +131,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

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-24 16:03               ` [PATCH v8 05/13] telemetry: add client feature and sockets Kevin Laatz
@ 2018-10-25 20:29                 ` Thomas Monjalon
  2018-10-25 20:41                   ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-25 20:29 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold

24/10/2018 18:03, Kevin Laatz:
> --- 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

I just discovered an issue when trying to compile for arm64
(with devtools/test-meson-builds.sh):

/usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld:
	cannot find -ljansson

I am worried because it is a real blocker.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 11/13] doc: add telemetry documentation
  2018-10-24 16:03               ` [PATCH v8 11/13] doc: add telemetry documentation Kevin Laatz
@ 2018-10-25 20:31                 ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-25 20:31 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold

24/10/2018 18:03, Kevin Laatz:
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> +Telemetry - EXPERIMENTAL
> +M: Kevin Laatz <kevin.laatz@intel.com>
> +F: lib/librte_telemetry/
> +F: usertools/dpdk-telemetry-client.py
> +F: doc/guides/howto/telemetry.rst

It would be a bit better to introduce it in previous patches when files
are created. I wanted to spread it in other patches myself,
but I stopped when encountering a build issue (patch 5).

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 03/13] telemetry: initial telemetry infrastructure
  2018-10-24 16:03               ` [PATCH v8 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
@ 2018-10-25 20:33                 ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-25 20:33 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold

We forgot to add rte_telemetry.h in doxygen:
	doc/api/doxy-api-index.md
	doc/api/doxy-api.conf.in

We need a section @file at the top of the header file
to make it appear in doxygen.


24/10/2018 18:03, Kevin Laatz:
> --- /dev/null
> +++ b/lib/librte_telemetry/rte_telemetry.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018 Intel Corporation
> + */
> +
> +#include <stdint.h>
> +
> +#ifndef _RTE_TELEMETRY_H_
> +#define _RTE_TELEMETRY_H_
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * 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_experimental
> +rte_telemetry_init(void);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Clean up and free memory.
> + *
> + * @return
> + *  0 on success
> + * @return
> + *  -EPERM on failure
> + */
> +int32_t __rte_experimental
> +rte_telemetry_cleanup(void);
> +
> +#endif

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-25 20:29                 ` Thomas Monjalon
@ 2018-10-25 20:41                   ` Thomas Monjalon
  2018-10-25 20:44                     ` Bruce Richardson
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-25 20:41 UTC (permalink / raw)
  To: Kevin Laatz
  Cc: dev, harry.van.haaren, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, bruce.richardson, Ciara Power, Brian Archbold

25/10/2018 22:29, Thomas Monjalon:
> 24/10/2018 18:03, Kevin Laatz:
> > --- 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
> 
> I just discovered an issue when trying to compile for arm64
> (with devtools/test-meson-builds.sh):
> 
> /usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld:
> 	cannot find -ljansson
> 
> I am worried because it is a real blocker.

The dependency must be optional.
Should we disable the library if jansson is not found?

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-25 20:41                   ` Thomas Monjalon
@ 2018-10-25 20:44                     ` Bruce Richardson
  2018-10-25 20:49                       ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Bruce Richardson @ 2018-10-25 20:44 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Kevin Laatz, dev, harry.van.haaren, stephen, gaetan.rivet,
	shreyansh.jain, mattias.ronnblom, Ciara Power, Brian Archbold

On Thu, Oct 25, 2018 at 10:41:53PM +0200, Thomas Monjalon wrote:
> 25/10/2018 22:29, Thomas Monjalon:
> > 24/10/2018 18:03, Kevin Laatz:
> > > --- 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
> > 
> > I just discovered an issue when trying to compile for arm64
> > (with devtools/test-meson-builds.sh):
> > 
> > /usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld:
> > 	cannot find -ljansson
> > 
> > I am worried because it is a real blocker.
> 
> The dependency must be optional.
> Should we disable the library if jansson is not found?
>
Yes, it should be.

Needs:

	jansson = cc.find_library('jansson', required: false)
	build = jansson.found()


/Bruce 

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-25 20:44                     ` Bruce Richardson
@ 2018-10-25 20:49                       ` Thomas Monjalon
  2018-10-25 21:16                         ` Richardson, Bruce
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-25 20:49 UTC (permalink / raw)
  To: Bruce Richardson
  Cc: Kevin Laatz, dev, harry.van.haaren, stephen, gaetan.rivet,
	shreyansh.jain, mattias.ronnblom, Ciara Power, Brian Archbold

25/10/2018 22:44, Bruce Richardson:
> On Thu, Oct 25, 2018 at 10:41:53PM +0200, Thomas Monjalon wrote:
> > 25/10/2018 22:29, Thomas Monjalon:
> > > 24/10/2018 18:03, Kevin Laatz:
> > > > --- 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
> > > 
> > > I just discovered an issue when trying to compile for arm64
> > > (with devtools/test-meson-builds.sh):
> > > 
> > > /usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld:
> > > 	cannot find -ljansson
> > > 
> > > I am worried because it is a real blocker.
> > 
> > The dependency must be optional.
> > Should we disable the library if jansson is not found?
> >
> Yes, it should be.
> 
> Needs:
> 
> 	jansson = cc.find_library('jansson', required: false)
> 	build = jansson.found()

And for the makefile?

It has been introduced (too early) in patch 3:
	LDLIBS += -ljansson
I think we must use pkg-config to check if jansson is found.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-25 20:49                       ` Thomas Monjalon
@ 2018-10-25 21:16                         ` Richardson, Bruce
  2018-10-25 23:58                           ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Richardson, Bruce @ 2018-10-25 21:16 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Laatz, Kevin, dev, Van Haaren, Harry, stephen, gaetan.rivet,
	shreyansh.jain, mattias.ronnblom, Ciara Power, Brian Archbold



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Thursday, October 25, 2018 9:50 PM
> To: Richardson, Bruce <bruce.richardson@intel.com>
> Cc: Laatz, Kevin <kevin.laatz@intel.com>; dev@dpdk.org; Van Haaren, Harry
> <harry.van.haaren@intel.com>; stephen@networkplumber.org;
> gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> mattias.ronnblom@ericsson.com; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>
> Subject: Re: [dpdk-dev] [PATCH v8 05/13] telemetry: add client feature and
> sockets
> 
> 25/10/2018 22:44, Bruce Richardson:
> > On Thu, Oct 25, 2018 at 10:41:53PM +0200, Thomas Monjalon wrote:
> > > 25/10/2018 22:29, Thomas Monjalon:
> > > > 24/10/2018 18:03, Kevin Laatz:
> > > > > --- 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
> > > >
> > > > I just discovered an issue when trying to compile for arm64 (with
> > > > devtools/test-meson-builds.sh):
> > > >
> > > > /usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-
> gnu/bin/ld:
> > > > 	cannot find -ljansson
> > > >
> > > > I am worried because it is a real blocker.
> > >
> > > The dependency must be optional.
> > > Should we disable the library if jansson is not found?
> > >
> > Yes, it should be.
> >
> > Needs:
> >
> > 	jansson = cc.find_library('jansson', required: false)
> > 	build = jansson.found()
> 
> And for the makefile?
> 
> It has been introduced (too early) in patch 3:
> 	LDLIBS += -ljansson
> I think we must use pkg-config to check if jansson is found.
> 

I didn't look at the makefile, because you never asked! :-)

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v8 05/13] telemetry: add client feature and sockets
  2018-10-25 21:16                         ` Richardson, Bruce
@ 2018-10-25 23:58                           ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-25 23:58 UTC (permalink / raw)
  To: Richardson, Bruce, Laatz, Kevin
  Cc: dev, Van Haaren, Harry, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold

25/10/2018 23:16, Richardson, Bruce:
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> > 25/10/2018 22:44, Bruce Richardson:
> > > On Thu, Oct 25, 2018 at 10:41:53PM +0200, Thomas Monjalon wrote:
> > > > 25/10/2018 22:29, Thomas Monjalon:
> > > > > 24/10/2018 18:03, Kevin Laatz:
> > > > > > --- 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
> > > > >
> > > > > I just discovered an issue when trying to compile for arm64 (with
> > > > > devtools/test-meson-builds.sh):
> > > > >
> > > > > /usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-
> > gnu/bin/ld:
> > > > > 	cannot find -ljansson
> > > > >
> > > > > I am worried because it is a real blocker.
> > > >
> > > > The dependency must be optional.
> > > > Should we disable the library if jansson is not found?
> > > >
> > > Yes, it should be.
> > >
> > > Needs:
> > >
> > > 	jansson = cc.find_library('jansson', required: false)
> > > 	build = jansson.found()
> > 
> > And for the makefile?
> > 
> > It has been introduced (too early) in patch 3:
> > 	LDLIBS += -ljansson
> > I think we must use pkg-config to check if jansson is found. 
> 
> I didn't look at the makefile, because you never asked! :-)

The code is already written in examples/power:
	https://patches.dpdk.org/patch/46984/

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v9 00/12] Introduce Telemetry Library
  2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
                                 ` (12 preceding siblings ...)
  2018-10-24 16:03               ` [PATCH v8 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
@ 2018-10-26 23:59               ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 01/12] eal: add option register infrastructure Harry van Haaren
                                   ` (12 more replies)
  13 siblings, 13 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Harry van Haaren

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

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

v5:
  - Moved the BUF_SIZE define to fix build (Harry)
  - Set default config for telemetry to 'n' (Harry)
  - Improved Doxygen comments (Thomas)
  - Cleaned up rte_param struct (Thomas)

v6:
  - Renamed rte_param to rte_option (Thomas)
  - Moved internal functions to eal_private.h (Gaetan)
  - Added fail check for pthread_attr_init() (Mattias)
  - Changed socket implementation to SOCK_SEQPACKET (Mattias)
  - Added check to avoid option duplicates (Gaetan)
  - Removed telemetry example from Doxygen comment (Gaetan)
  - General Doxygen clean-up (Thomas)
  - General code clean-up (Mattias)

v7:
  - Fix object leak (Mattias)
  - General clean-up (Mattias)
  - Fix rte_option header define
  - Added release note update
  - Rebased

v8:
  - Added missing experimental tags
  - Added Gaetan's Ack from ML

v9:
  - Added doxygen to build (Thomas)
  - Added doxygen @file tag (Thomas)
  - Detect jansson library dynamically with Make/pkg-config (Thomas)
  - Make jansson optional build-dep for Meson (Thomas/Bruce)


Ciara Power (9):
  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 ability to disable selftest
  doc: add telemetry documentation
  usertools: add client python script for telemetry

Kevin Laatz (3):
  eal: add option 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/api/doxy-api-index.md                     |    3 +-
 doc/api/doxy-api.conf.in                      |    1 +
 doc/guides/howto/index.rst                    |    1 +
 doc/guides/howto/telemetry.rst                |   85 +
 doc/guides/rel_notes/release_18_11.rst        |    6 +
 lib/Makefile                                  |    2 +
 lib/librte_eal/bsdapp/eal/Makefile            |    1 +
 lib/librte_eal/bsdapp/eal/eal.c               |   16 +-
 lib/librte_eal/common/Makefile                |    1 +
 lib/librte_eal/common/eal_filesystem.h        |   15 +-
 lib/librte_eal/common/eal_private.h           |   21 +
 lib/librte_eal/common/include/rte_eal.h       |    9 +
 lib/librte_eal/common/include/rte_option.h    |   63 +
 lib/librte_eal/common/meson.build             |    2 +
 lib/librte_eal/common/rte_option.c            |   54 +
 lib/librte_eal/linuxapp/eal/Makefile          |    1 +
 lib/librte_eal/linuxapp/eal/eal.c             |   16 +-
 lib/librte_eal/rte_eal_version.map            |    2 +
 lib/librte_telemetry/Makefile                 |   30 +
 lib/librte_telemetry/meson.build              |   15 +
 lib/librte_telemetry/rte_telemetry.c          | 1815 +++++++++++++++++
 lib/librte_telemetry/rte_telemetry.h          |   66 +
 lib/librte_telemetry/rte_telemetry_internal.h |   81 +
 lib/librte_telemetry/rte_telemetry_parser.c   |  586 ++++++
 lib/librte_telemetry/rte_telemetry_parser.h   |   14 +
 .../rte_telemetry_parser_test.c               |  534 +++++
 .../rte_telemetry_parser_test.h               |   39 +
 .../rte_telemetry_socket_tests.h              |   36 +
 .../rte_telemetry_version.map                 |   10 +
 lib/meson.build                               |    3 +-
 meson.build                                   |    2 +
 mk/rte.app.mk                                 |    3 +-
 mk/rte.vars.mk                                |    6 +
 usertools/dpdk-telemetry-client.py            |  116 ++
 43 files changed, 3661 insertions(+), 21 deletions(-)
 create mode 100644 doc/guides/howto/telemetry.rst
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.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.17.1

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v9 01/12] eal: add option register infrastructure
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 02/12] eal: make get runtime dir function public Harry van Haaren
                                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Kevin Laatz

From: Kevin Laatz <kevin.laatz@intel.com>

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 <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>
---
 lib/librte_eal/bsdapp/eal/Makefile         |  1 +
 lib/librte_eal/bsdapp/eal/eal.c            | 14 ++++-
 lib/librte_eal/common/Makefile             |  1 +
 lib/librte_eal/common/eal_private.h        | 21 ++++++++
 lib/librte_eal/common/include/rte_option.h | 63 ++++++++++++++++++++++
 lib/librte_eal/common/meson.build          |  2 +
 lib/librte_eal/common/rte_option.c         | 54 +++++++++++++++++++
 lib/librte_eal/linuxapp/eal/Makefile       |  1 +
 lib/librte_eal/linuxapp/eal/eal.c          | 14 ++++-
 lib/librte_eal/rte_eal_version.map         |  1 +
 10 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d19f53c1e..bfeddaadc 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -67,6 +67,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_option.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 f94c5c5f4..11cbda964 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -791,6 +800,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca68826f..87d8c455d 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_option.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/eal_private.h b/lib/librte_eal/common/eal_private.h
index b189d675d..442c6dc48 100644
--- a/lib/librte_eal/common/eal_private.h
+++ b/lib/librte_eal/common/eal_private.h
@@ -349,4 +349,25 @@ dev_sigbus_handler_register(void);
 int
 dev_sigbus_handler_unregister(void);
 
+/**
+ * Check if the option is registered.
+ *
+ * @param option
+ *  The option to be parsed.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -1 on fail
+ */
+int
+rte_option_parse(const char *opt);
+
+/**
+ * Iterate through the registered options and execute the associated
+ * callback if enabled.
+ */
+void
+rte_option_init(void);
+
 #endif /* _EAL_PRIVATE_H_ */
diff --git a/lib/librte_eal/common/include/rte_option.h b/lib/librte_eal/common/include/rte_option.h
new file mode 100644
index 000000000..8957b970c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_option.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_OPTION_H__
+#define __INCLUDE_RTE_OPTION_H__
+
+/**
+ * @file
+ *
+ * This API offers the ability to register options to the EAL command line and
+ * map those options to functions that will be executed at the end of EAL
+ * initialization. These options will be available as part of the EAL command
+ * line of applications and are dynamically managed.
+ *
+ * This is used primarily by DPDK libraries offering command line options.
+ * Currently, this API is limited to registering options without argument.
+ *
+ * The register API can be used to resolve circular dependency issues
+ * between EAL and the library. The library uses EAL, but is also initialized
+ * by EAL. Hence, EAL depends on the init function of the library. The API
+ * introduced in rte_option allows us to register the library init with EAL
+ * (passing a function pointer) and avoid the circular dependency.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_option_cb)(void);
+
+/*
+ * Structure describing the EAL command line option being registered.
+ */
+struct rte_option {
+	TAILQ_ENTRY(rte_option) next; /**< Next entry in the list. */
+	char *opt_str;             /**< The option name. */
+	rte_option_cb cb;          /**< Function called when option is used. */
+	int enabled;               /**< Set when the option is used. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register an option to the EAL command line.
+ * When recognized, the associated function will be executed at the end of EAL
+ * initialization.
+ *
+ * The associated structure must be available the whole time this option is
+ * registered (i.e. not stack memory).
+ *
+ * @param opt
+ *  Structure describing the option to parse.
+ */
+void __rte_experimental
+rte_option_register(struct rte_option *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 04c414356..2a10d57d8 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -34,6 +34,7 @@ common_sources = files(
 	'malloc_mp.c',
 	'rte_keepalive.c',
 	'rte_malloc.c',
+	'rte_option.c',
 	'rte_reciprocal.c',
 	'rte_service.c'
 )
@@ -71,6 +72,7 @@ common_headers = files(
 	'include/rte_malloc_heap.h',
 	'include/rte_memory.h',
 	'include/rte_memzone.h',
+	'include/rte_option.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_option.c b/lib/librte_eal/common/rte_option.c
new file mode 100644
index 000000000..02d59a869
--- /dev/null
+++ b/lib/librte_eal/common/rte_option.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_option.h>
+
+#include "eal_private.h"
+
+TAILQ_HEAD(rte_option_list, rte_option);
+
+struct rte_option_list rte_option_list =
+	TAILQ_HEAD_INITIALIZER(rte_option_list);
+
+static struct rte_option *option;
+
+int
+rte_option_parse(const char *opt)
+{
+	/* Check if the option is registered */
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt, option->opt_str) == 0) {
+			option->enabled = 1;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void __rte_experimental
+rte_option_register(struct rte_option *opt)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt->opt_str, option->opt_str) == 0)
+			RTE_LOG(INFO, EAL, "Option %s has already been registered.",
+					opt->opt_str);
+			return;
+	}
+
+	TAILQ_INSERT_HEAD(&rte_option_list, opt, next);
+}
+
+void
+rte_option_init(void)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (option->enabled)
+			option->cb();
+	}
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 728088594..51deb5797 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -75,6 +75,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_option.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 76536bae2..930070e43 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -1080,6 +1089,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index f4a2b38fd..5b6d91ffd 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -350,6 +350,7 @@ EXPERIMENTAL {
 	rte_mp_request_sync;
 	rte_mp_request_async;
 	rte_mp_sendmsg;
+	rte_option_register;
 	rte_service_lcore_attr_get;
 	rte_service_lcore_attr_reset_all;
 	rte_service_may_be_active;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 02/12] eal: make get runtime dir function public
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 01/12] eal: add option register infrastructure Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
                                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Kevin Laatz

From: Kevin Laatz <kevin.laatz@intel.com>

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_eal/bsdapp/eal/eal.c         |  2 +-
 lib/librte_eal/common/eal_filesystem.h  | 15 ++++++++-------
 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, 20 insertions(+), 9 deletions(-)

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 11cbda964..21997ced8 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 de05febf4..b3e8ae5ea 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,8 @@ 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 +67,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 +79,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 +100,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 6514a9fe6..a0cedd573 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -507,6 +507,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 930070e43..7b11375f4 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 5b6d91ffd..28bfa4950 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -259,6 +259,7 @@ DPDK_18.08 {
 DPDK_18.11 {
 	global:
 
+	rte_eal_get_runtime_dir;
 	rte_eal_hotplug_add;
 	rte_eal_hotplug_remove;
 	rte_strscpy;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 03/12] telemetry: initial telemetry infrastructure
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 01/12] eal: add option register infrastructure Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 02/12] eal: make get runtime dir function public Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-27  1:56                   ` Thomas Monjalon
  2018-10-26 23:59                 ` [PATCH v9 04/12] telemetry: add initial connection socket Harry van Haaren
                                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz,
	Radu Nicolau

From: Ciara Power <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 option. 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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

---

v9:
- Add telemetry.h to doxygen index/conf (Thomas)
- Add @file section to telemetry.h for Doxygen (Thomas)
- Move -ljansson to patch that introduces json_* calls (Thomas)
---
 config/common_base                            |   5 +
 doc/api/doxy-api-index.md                     |   3 +-
 doc/api/doxy-api.conf.in                      |   1 +
 lib/Makefile                                  |   2 +
 lib/librte_telemetry/Makefile                 |  27 ++++
 lib/librte_telemetry/meson.build              |   7 +
 lib/librte_telemetry/rte_telemetry.c          | 123 ++++++++++++++++++
 lib/librte_telemetry/rte_telemetry.h          |  51 ++++++++
 lib/librte_telemetry/rte_telemetry_internal.h |  32 +++++
 .../rte_telemetry_version.map                 |   8 ++
 lib/meson.build                               |   2 +-
 mk/rte.app.mk                                 |   3 +-
 mk/rte.vars.mk                                |   6 +
 13 files changed, 267 insertions(+), 3 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 38beaabb3..4236b7f46 100644
--- a/config/common_base
+++ b/config/common_base
@@ -756,6 +756,11 @@ CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
 CONFIG_RTE_LIBRTE_HASH=y
 CONFIG_RTE_LIBRTE_HASH_DEBUG=n
 
+#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=y
+
 #
 # Compile librte_efd
 #
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index a3039d168..0d0b9bd6c 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -159,7 +159,8 @@ The public API headers are grouped by topics:
   [hexdump]            (@ref rte_hexdump.h),
   [debug]              (@ref rte_debug.h),
   [log]                (@ref rte_log.h),
-  [errno]              (@ref rte_errno.h)
+  [errno]              (@ref rte_errno.h),
+  [telemetry]          (@ref rte_telemetry.h)
 
 - **misc**:
   [EAL config]         (@ref rte_eal.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 3b652ac9c..77ba327a8 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -56,6 +56,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_sched \
                           @TOPDIR@/lib/librte_security \
                           @TOPDIR@/lib/librte_table \
+                          @TOPDIR@/lib/librte_telemetry \
                           @TOPDIR@/lib/librte_timer \
                           @TOPDIR@/lib/librte_vhost
 INPUT                   += @API_EXAMPLES@
diff --git a/lib/Makefile b/lib/Makefile
index 062cbdfd7..b7370ef97 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -106,6 +106,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 000000000..a2d4ff166
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,27 @@
+# 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
+
+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 000000000..7716076a9
--- /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 000000000..7f4ad0342
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,123 @@
+/* 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_option.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_experimental
+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);
+
+	ret = pthread_attr_init(&attr);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Pthread attribute init failed");
+		return -EPERM;
+	}
+
+	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_experimental
+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_option option = {
+	.opt_str = "--telemetry",
+	.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_option_register(&option);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 000000000..97674ae2d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * @file
+ * RTE Telemetry
+ *
+ * The telemetry library provides a method to retrieve statistics from
+ * DPDK by sending a JSON encoded message over a socket. DPDK will send
+ * a JSON encoded response containing telemetry data.
+ ***/
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 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_experimental
+rte_telemetry_init(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clean up and free memory.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -EPERM on failure
+ */
+int32_t __rte_experimental
+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 000000000..4e810a84c
--- /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 000000000..bbcd9a796
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,8 @@
+EXPERIMENTAL {
+	global:
+
+	rte_telemetry_cleanup;
+	rte_telemetry_init;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 2b903fa37..9d1f353d2 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -25,7 +25,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 c0036daf8..c5aaa9da5 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -51,7 +51,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += --whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += --no-whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_JOBSTATS)       += -lrte_jobstats
-_LDLIBS-$(CONFIG_RTE_LIBRTE_METRICS)        += -lrte_metrics
 _LDLIBS-$(CONFIG_RTE_LIBRTE_BITRATE)        += -lrte_bitratestats
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS)  += -lrte_latencystats
 _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
@@ -80,6 +79,8 @@ _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_METRICS)        += -lrte_metrics
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += -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
diff --git a/mk/rte.vars.mk b/mk/rte.vars.mk
index 07b0db127..603972c3c 100644
--- a/mk/rte.vars.mk
+++ b/mk/rte.vars.mk
@@ -52,6 +52,12 @@ ifneq ($(CONFIG_RTE_LIBRTE_E1000_PMD),y)
   CONFIG_RTE_LIBRTE_E1000_PMD = $(CONFIG_RTE_LIBRTE_EM_PMD)
 endif
 
+JANSSON := $(shell pkg-config --exists jansson; echo $$?)
+ifneq ($(JANSSON),0)
+$(warning Jansson not found, disabling RTE_LIBRTE_TELEMETRY)
+CONFIG_RTE_LIBRTE_TELEMETRY = n
+endif
+
 ifeq ($(RTE_ARCH),)
 $(error RTE_ARCH is not defined)
 endif
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 04/12] telemetry: add initial connection socket
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (2 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 05/12] telemetry: add client feature and sockets Harry van Haaren
                                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 7f4ad0342..e9b3330ca 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,163 @@
  */
 
 #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_option.h>
+#include <rte_string_fns.h>
 
 #include "rte_telemetry.h"
 #include "rte_telemetry_internal.h"
 
+#define BUF_SIZE 1024
 #define SLEEP_TIME 10
 
 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) {
+		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 +167,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 +196,67 @@ 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;
+	char socket_path[BUF_SIZE];
+
+	if (telemetry == NULL)
+		return -1;
+
+	telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (telemetry->server_fd == -1) {
+		TELEMETRY_LOG_ERR("Failed to open socket");
+		return -1;
+	}
+
+	ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		goto close_socket;
+	}
+
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+	unlink(socket_path);
+
+	if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
+		sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Socket binding error");
+		goto close_socket;
+	}
+
+	return 0;
+
+close_socket:
+	if (close(telemetry->server_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		return -EPERM;
+	}
+
+	return -1;
+}
+
 int32_t __rte_experimental
 rte_telemetry_init()
 {
@@ -77,6 +284,14 @@ rte_telemetry_init()
 		return -EPERM;
 	}
 
+	ret = rte_telemetry_create_socket(static_telemetry);
+	if (ret < 0) {
+		ret = rte_telemetry_cleanup();
+		if (ret < 0)
+			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
+		return -EPERM;
+	}
+
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
 		(void *)static_telemetry);
@@ -95,11 +310,21 @@ rte_telemetry_init()
 int32_t __rte_experimental
 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 4e810a84c..569d56ab8 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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 05/12] telemetry: add client feature and sockets
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (3 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 04/12] telemetry: add initial connection socket Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 06/12] telemetry: add parser for client socket messages Harry van Haaren
                                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

---

v9:
- Meson build disables if jansson not available (Thomas/Bruce)
- Add link to Jansson library (was in patch 3) (Thomas)
- Use pkg-config to get link flag (was -ljansson directly)
---
 lib/librte_telemetry/Makefile                 |   1 +
 lib/librte_telemetry/meson.build              |   7 +
 lib/librte_telemetry/rte_telemetry.c          | 370 +++++++++++++++++-
 lib/librte_telemetry/rte_telemetry_internal.h |  25 ++
 4 files changed, 400 insertions(+), 3 deletions(-)

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index a2d4ff166..5ce1ad40c 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -13,6 +13,7 @@ CFLAGS += -DALLOW_EXPERIMENTAL_API
 LDLIBS += -lrte_eal -lrte_ethdev
 LDLIBS += -lrte_metrics
 LDLIBS += -lpthread
+LDLIBS += $(shell pkg-config --libs jansson)
 
 EXPORT_MAP := rte_telemetry_version.map
 
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076a9..7b939805e 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,10 @@ 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: false)
+if jansson.found()
+	ext_deps += jansson
+else
+	build = false
+endif
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index e9b3330ca..3c8b922f5 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>
@@ -18,6 +19,7 @@
 #include "rte_telemetry_internal.h"
 
 #define BUF_SIZE 1024
+#define ACTION_POST 1
 #define SLEEP_TIME 10
 
 static telemetry_impl *static_telemetry;
@@ -39,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;
 }
 
@@ -115,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 +216,38 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
 	return 0;
 }
 
+static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+	char buf[BUF_SIZE];
+	int ret, buffer_read;
+
+	buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	} else if (buffer_read == 0) {
+		goto close_socket;
+	} else {
+		buf[buffer_read] = '\0';
+		ret = rte_telemetry_parse_client_message(telemetry, buf);
+		if (ret < 0)
+			TELEMETRY_LOG_WARN("Parse message failed");
+		goto close_socket;
+	}
+
+close_socket:
+	if (close(telemetry->accept_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		free(telemetry);
+		return -EPERM;
+	}
+	telemetry->accept_fd = 0;
+
+	return 0;
+}
+
 static int32_t
 rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
 {
@@ -141,8 +259,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);
@@ -151,6 +269,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);
+
+		if (bytes > 0) {
+			client_buf[bytes] = '\0';
+			telemetry->request_client = client;
+		}
 	}
 
 	return 0;
@@ -173,6 +316,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;
 }
 
@@ -291,6 +440,7 @@ rte_telemetry_init()
 			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
 		return -EPERM;
 	}
+	TAILQ_INIT(&static_telemetry->client_list_head);
 
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -307,11 +457,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_experimental
 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) {
@@ -328,6 +506,192 @@ 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;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+		return -ENODEV;
+	}
+
+	if (client_path == NULL) {
+		TELEMETRY_LOG_ERR("Invalid client path");
+		return -EINVAL;
+	}
+
+	telemetry_client *client;
+	TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
+		if (strcmp(client_path, client->file_path) == 0) {
+			TELEMETRY_LOG_WARN("'%s' already registered",
+					client_path);
+			return -EINVAL;
+		}
+	}
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd == -1) {
+		TELEMETRY_LOG_ERR("Client socket error");
+		return -EACCES;
+	}
+
+	ret = rte_telemetry_set_socket_nonblock(fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		return -EPERM;
+	}
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	telemetry_client *new_client = malloc(sizeof(telemetry_client));
+	new_client->file_path = strdup(client_path);
+	new_client->fd = fd;
+
+	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
+		TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
+				client_path);
+		ret = rte_telemetry_client_cleanup(new_client);
+		if (ret < 0) {
+			TELEMETRY_LOG_ERR("Client cleanup failed");
+			return -EPERM;
+		}
+		return -EINVAL;
+	}
+
+	TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
+{
+	int ret, action_int;
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+
+	if (root == NULL) {
+		TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+				error.text);
+		goto fail;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+		goto fail;
+	}
+
+	json_t *action = json_object_get(root, "action");
+	if (action == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have action field");
+		goto fail;
+	} else if (!json_is_integer(action)) {
+		TELEMETRY_LOG_WARN("Action value is not an integer");
+		goto fail;
+	}
+
+	json_t *command = json_object_get(root, "command");
+	if (command == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have command field");
+		goto fail;
+	} else if (!json_is_string(command)) {
+		TELEMETRY_LOG_WARN("Command value is not a string");
+		goto fail;
+	}
+
+	action_int = json_integer_value(action);
+	if (action_int != ACTION_POST) {
+		TELEMETRY_LOG_WARN("Invalid action code");
+		goto fail;
+	}
+
+	if (strcmp(json_string_value(command), "clients") != 0) {
+		TELEMETRY_LOG_WARN("Invalid command");
+		goto fail;
+	}
+
+	json_t *data = json_object_get(root, "data");
+	if (data == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have data field");
+		goto fail;
+	}
+
+	json_t *client_path = json_object_get(data, "client_path");
+	if (client_path == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have client_path field");
+		goto fail;
+	}
+
+	if (!json_is_string(client_path)) {
+		TELEMETRY_LOG_WARN("Client_path value is not a string");
+		goto fail;
+	}
+
+	ret = rte_telemetry_register_client(telemetry,
+			json_string_value(client_path));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not register client");
+		telemetry->register_fail_count++;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	TELEMETRY_LOG_WARN("Client attempted to register with invalid message");
+	json_decref(root);
+	return -1;
+}
+
 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 569d56ab8..e3292cf40 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
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 06/12] telemetry: add parser for client socket messages
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (4 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 05/12] telemetry: add client feature and sockets Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 07/12] telemetry: update metrics before sending stats Harry van Haaren
                                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

v9:
- Add rte_telemetry_parse() to version map file for shared builds
---
 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   |  14 +
 .../rte_telemetry_version.map                 |   1 +
 7 files changed, 608 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 5ce1ad40c..562bbf94b 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 7b939805e..e459d0b80 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'
 
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 3c8b922f5..cac788438 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
@@ -283,6 +284,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,6 +295,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
 		if (bytes > 0) {
 			client_buf[bytes] = '\0';
 			telemetry->request_client = client;
+			ret = rte_telemetry_parse(telemetry, client_buf);
+			if (ret < 0) {
+				TELEMETRY_LOG_WARN("Parse socket input failed: %i",
+						ret);
+				return -1;
+			}
 		}
 	}
 
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf40..86a5ba15e 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 000000000..556abbe4a
--- /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_experimental
+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 000000000..b7051945b
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include "rte_telemetry_internal.h"
+#include "rte_compat.h"
+
+#ifndef _RTE_TELEMETRY_PARSER_H_
+#define _RTE_TELEMETRY_PARSER_H_
+
+int32_t __rte_experimental
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
index bbcd9a796..fb0b5be62 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	rte_telemetry_cleanup;
 	rte_telemetry_init;
+	rte_telemetry_parse;
 
 	local: *;
 };
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 07/12] telemetry: update metrics before sending stats
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (5 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 06/12] telemetry: add parser for client socket messages Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 08/12] telemetry: format json response when " Harry van Haaren
                                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 cac788438..7616e7c91 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 86a5ba15e..0082cb2ca 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 556abbe4a..03a58a2fd 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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 08/12] telemetry: format json response when sending stats
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (6 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 07/12] telemetry: update metrics before sending stats Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 09/12] telemetry: add ability to disable selftest Harry van Haaren
                                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_telemetry/Makefile                 |   1 +
 lib/librte_telemetry/meson.build              |   4 +-
 lib/librte_telemetry/rte_telemetry.c          | 962 +++++++++++++++++-
 lib/librte_telemetry/rte_telemetry.h          |  15 +
 lib/librte_telemetry/rte_telemetry_internal.h |   3 +
 .../rte_telemetry_parser_test.c               | 534 ++++++++++
 .../rte_telemetry_parser_test.h               |  39 +
 .../rte_telemetry_socket_tests.h              |  36 +
 .../rte_telemetry_version.map                 |   1 +
 9 files changed, 1591 insertions(+), 4 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 562bbf94b..9feff6832 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 e459d0b80..b3bbf5bd0 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'
 
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 7616e7c91..5b66b71c4 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)
 {
@@ -190,7 +209,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 +221,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 +558,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");
@@ -335,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);
@@ -347,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;
 }
@@ -834,6 +1171,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_SEQPACKET, 0);
+	struct sockaddr_un addr = {0};
+
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failure");
+		return -1;
+	}
+
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path));
+	unlink(valid_client_path);
+
+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Test socket binding failure");
+		return -1;
+	}
+
+	if (listen(sockfd, 1) < 0) {
+		TELEMETRY_LOG_ERR("Listen failure");
+		return -1;
+	}
+
+	return sockfd;
+}
+
+int32_t __rte_experimental
+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_SEQPACKET, 0);
+	*recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+
+	listen(telemetry->server_fd, 5);
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+
+	ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not connect socket");
+		return -1;
+	}
+
+	telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	unlink(client_path);
+
+	ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not bind socket");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int32_t
+rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct)
+{
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+	int arraylen, i;
+	json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj,
+	       *statsArrayObj;
+
+	stats = NULL;
+	port = NULL;
+	name = NULL;
+
+	if (buf == NULL) {
+		TELEMETRY_LOG_ERR("JSON message is NULL");
+		return -EINVAL;
+	}
+
+	if (root == NULL) {
+		TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s",
+				error.text);
+		return -EPERM;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_ERR("JSON Request is not a JSON object");
+		json_decref(root);
+		return -EINVAL;
+	}
+
+	status = json_object_get(root, "status_code");
+	if (!status) {
+		TELEMETRY_LOG_ERR("Request does not have status field");
+		return -EINVAL;
+	} else if (!json_is_string(status)) {
+		TELEMETRY_LOG_ERR("Status value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->status_code = strdup(json_string_value(status));
+
+	dataArray = json_object_get(root, "data");
+	if (dataArray == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have data field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(dataArray);
+	if (arraylen == 0) {
+		json_data_struct->data = "null";
+		return -EINVAL;
+	}
+
+	for (i = 0; i < arraylen; i++) {
+		dataArrayObj = json_array_get(dataArray, i);
+		port = json_object_get(dataArrayObj, "port");
+		stats = json_object_get(dataArrayObj, "stats");
+	}
+
+	if (port == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have port field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(port)) {
+		TELEMETRY_LOG_ERR("Port value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->port = json_integer_value(port);
+
+	if (stats == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have stats field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(stats);
+	for (i = 0; i < arraylen; i++) {
+		statsArrayObj = json_array_get(stats, i);
+		name = json_object_get(statsArrayObj, "name");
+		value = json_object_get(statsArrayObj, "value");
+	}
+
+	if (name == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have name field");
+		return -EINVAL;
+	}
+
+	if (!json_is_string(name)) {
+		TELEMETRY_LOG_ERR("Stat name value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_name = strdup(json_string_value(name));
+
+	if (value == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have value field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(value)) {
+		TELEMETRY_LOG_ERR("Stat value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_value = json_integer_value(value);
+
+	return 0;
+}
+
+static void
+rte_telemetry_free_test_data(struct json_data *data)
+{
+	free(data->status_code);
+	free(data->stat_name);
+	free(data);
+}
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	int port = 0;
+	int value = 0;
+	int fail_count = 0;
+	int buffer_read = 0;
+	char buf[BUF_SIZE];
+	struct json_data *data_struct;
+	errno = 0;
+	const char *status = "Status OK: 200";
+	const char *name = "rx_good_packets";
+	const char *valid_json_message = "{\"action\":0,\"command\":"
+	"\"ports_stats_values_by_name\",\"data\":{\"ports\""
+	":[0],\"stats\":[\"rx_good_packets\"]}}";
+
+	ret = send(fd, valid_json_message, strlen(valid_json_message), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not parse stats");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->port != port) {
+		TELEMETRY_LOG_ERR("Port is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->stat_name, name) != 0) {
+		TELEMETRY_LOG_ERR("Stat name is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->stat_value != value) {
+		TELEMETRY_LOG_ERR("Stat value is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	const char *invalid_json = "{]";
+	const char *status = "Status Error: Unknown";
+	const char *data = "null";
+	struct json_data *data_struct;
+	int buffer_read = 0;
+	errno = 0;
+
+	ret = send(fd, invalid_json, strlen(invalid_json), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0)
+		TELEMETRY_LOG_ERR("Could not parse stats");
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->data, data) != 0) {
+		TELEMETRY_LOG_ERR("Data status is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	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 97674ae2d..119db16fe 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -48,4 +48,19 @@ rte_telemetry_init(void);
 int32_t __rte_experimental
 rte_telemetry_cleanup(void);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 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_experimental
+rte_telemetry_selftest(void);
+
 #endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 0082cb2ca..de7afda30 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 000000000..5fe93fa6e
--- /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_SEQPACKET, 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 000000000..6ada85276
--- /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 000000000..db9167c5d
--- /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 fb0b5be62..fa62d7718 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_telemetry_cleanup;
 	rte_telemetry_init;
 	rte_telemetry_parse;
+	rte_telemetry_selftest;
 
 	local: *;
 };
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 09/12] telemetry: add ability to disable selftest
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (7 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 08/12] telemetry: format json response when " Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 10/12] doc: add telemetry documentation Harry van Haaren
                                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 5b66b71c4..2e27491b5 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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 10/12] doc: add telemetry documentation
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (8 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 09/12] telemetry: add ability to disable selftest Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 11/12] usertools: add client python script for telemetry Harry van Haaren
                                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 and a release notes update for
telemetry.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Marko Kovacevic <marko.kovacevic@intel.com>
---
 MAINTAINERS                            |  5 ++
 doc/guides/howto/index.rst             |  1 +
 doc/guides/howto/telemetry.rst         | 85 ++++++++++++++++++++++++++
 doc/guides/rel_notes/release_18_11.rst |  6 ++
 4 files changed, 97 insertions(+)
 create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 17ee5b9d3..be4ceb9b8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1208,6 +1208,11 @@ F: test/bpf/
 F: test/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Telemetry - EXPERIMENTAL
+M: Kevin Laatz <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 e13a090c3..a642a2be1 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 000000000..3fcb0619e
--- /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.
diff --git a/doc/guides/rel_notes/release_18_11.rst b/doc/guides/rel_notes/release_18_11.rst
index c6256939b..ed7994241 100644
--- a/doc/guides/rel_notes/release_18_11.rst
+++ b/doc/guides/rel_notes/release_18_11.rst
@@ -256,6 +256,12 @@ New Features
   the specified port. The port must be stopped before the command call in order
   to reconfigure queues.
 
+* **Added Telemetry API.**
+
+  Added the telemetry API which allows applications to transparently expose
+  their telemetry via a UNIX socket in JSON. The JSON can be consumed by any
+  Service Assurance agent, such as CollectD.
+
 * **Add a new sample for vDPA**
 
   The vdpa sample application creates vhost-user sockets by using the
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 11/12] usertools: add client python script for telemetry
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (9 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 10/12] doc: add telemetry documentation Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-26 23:59                 ` [PATCH v9 12/12] build: add dependency on telemetry to apps in meson Harry van Haaren
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 000000000..6dcf62bac
--- /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_SEQPACKET)
+        self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+        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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v9 12/12] build: add dependency on telemetry to apps in meson
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (10 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 11/12] usertools: add client python script for telemetry Harry van Haaren
@ 2018-10-26 23:59                 ` Harry van Haaren
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-26 23:59 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Kevin Laatz, Radu Nicolau

From: Kevin Laatz <kevin.laatz@intel.com>

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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/librte_telemetry/meson.build | 1 +
 lib/meson.build                  | 1 +
 meson.build                      | 2 ++
 10 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/app/meson.build b/app/meson.build
index e68d949e9..a9a026bbf 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -29,7 +29,7 @@ foreach app:apps
 	# use "deps" for internal DPDK dependencies, and "ext_deps" for
 	# external package/library requirements
 	ext_deps = []
-	deps = []
+	deps = dpdk_app_link_libraries
 
 	subdir(name)
 
@@ -43,7 +43,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 988cb4eb2..116c27f02 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 9c148e36e..a52b2ee4a 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 653907ded..eb8cc0499 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 eacd7a0f5..d735b186f 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 cd66618e9..6006c60f9 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -24,7 +24,7 @@ sources = files('cmdline.c',
 	'txonly.c',
 	'util.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 6f9228c87..275f00b60 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/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index b3bbf5bd0..9492f544e 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -9,6 +9,7 @@ cflags += '-DALLOW_EXPERIMENTAL_API'
 jansson = cc.find_library('jansson', required: false)
 if jansson.found()
 	ext_deps += jansson
+	dpdk_app_link_libraries += ['telemetry']
 else
 	build = false
 endif
diff --git a/lib/meson.build b/lib/meson.build
index 9d1f353d2..c0cc2d86e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -132,6 +132,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 c9af33532..b1e6eab6a 100644
--- a/meson.build
+++ b/meson.build
@@ -12,8 +12,10 @@ project('DPDK', 'C',
 cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
+dpdk_static_libraries = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
+dpdk_app_link_libraries = []
 
 driver_install_path = join_paths(get_option('libdir'), 'dpdk/drivers')
 eal_pmd_path = join_paths(get_option('prefix'), driver_install_path)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v9 03/12] telemetry: initial telemetry infrastructure
  2018-10-26 23:59                 ` [PATCH v9 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
@ 2018-10-27  1:56                   ` Thomas Monjalon
  2018-10-27  2:19                     ` Van Haaren, Harry
  0 siblings, 1 reply; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-27  1:56 UTC (permalink / raw)
  To: Harry van Haaren, Kevin Laatz, Radu Nicolau, david.hunt
  Cc: dev, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold

27/10/2018 01:59, Harry van Haaren:
> --- a/mk/rte.vars.mk
> +++ b/mk/rte.vars.mk
> +JANSSON := $(shell pkg-config --exists jansson; echo $$?)
> +ifneq ($(JANSSON),0)
> +$(warning Jansson not found, disabling RTE_LIBRTE_TELEMETRY)
> +CONFIG_RTE_LIBRTE_TELEMETRY = n
> +endif

It fails for cross-compilation.
Example:
When compiling i686 on x86_64 host, no error with
	pkg-config --exists jansson
but fails when linking:
	/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/8.2.1/../../../libjansson.so when searching for -ljansson
	/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/8.2.1/../../../libjansson.a when searching for -ljansson
	/usr/bin/ld: skipping incompatible /usr/lib/libjansson.so when searching for -ljansson
	/usr/bin/ld: skipping incompatible /usr/lib/libjansson.a when searching for -ljansson
	/usr/bin/ld: cannot find -ljansson

Note: there is the same issue with examples/vm_power_manager/Makefile

I start thinking it is not reasonnable to enable an optional dependency
in our Makefiles. Perhaps the only solution is to use meson...

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v9 03/12] telemetry: initial telemetry infrastructure
  2018-10-27  1:56                   ` Thomas Monjalon
@ 2018-10-27  2:19                     ` Van Haaren, Harry
  2018-10-27  2:33                       ` Thomas Monjalon
  0 siblings, 1 reply; 220+ messages in thread
From: Van Haaren, Harry @ 2018-10-27  2:19 UTC (permalink / raw)
  To: Thomas Monjalon, Richardson, Bruce
  Cc: dev, stephen, gaetan.rivet, shreyansh.jain, mattias.ronnblom,
	Laatz, Kevin, Nicolau, Radu, Hunt, David



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Friday, October 26, 2018 6:56 PM
> To: Van Haaren, Harry <harry.van.haaren@intel.com>; Laatz, Kevin
> <kevin.laatz@intel.com>; Nicolau, Radu <radu.nicolau@intel.com>; Hunt, David
> <david.hunt@intel.com>
> Cc: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>;
> stephen@networkplumber.org; gaetan.rivet@6wind.com; shreyansh.jain@nxp.com;
> mattias.ronnblom@ericsson.com; Ciara Power <ciara.power@intel.com>; Brian
> Archbold <brian.archbold@intel.com>
> Subject: Re: [dpdk-dev] [PATCH v9 03/12] telemetry: initial telemetry
> infrastructure
> 
> 27/10/2018 01:59, Harry van Haaren:
> > --- a/mk/rte.vars.mk
> > +++ b/mk/rte.vars.mk
> > +JANSSON := $(shell pkg-config --exists jansson; echo $$?)
> > +ifneq ($(JANSSON),0)
> > +$(warning Jansson not found, disabling RTE_LIBRTE_TELEMETRY)
> > +CONFIG_RTE_LIBRTE_TELEMETRY = n
> > +endif
> 
> It fails for cross-compilation.
> Example:
> When compiling i686 on x86_64 host, no error with
> 	pkg-config --exists jansson
> but fails when linking:
> 	/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-pc-linux-
> gnu/8.2.1/../../../libjansson.so when searching for -ljansson
> 	/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-pc-linux-
> gnu/8.2.1/../../../libjansson.a when searching for -ljansson
> 	/usr/bin/ld: skipping incompatible /usr/lib/libjansson.so when searching
> for -ljansson
> 	/usr/bin/ld: skipping incompatible /usr/lib/libjansson.a when searching
> for -ljansson
> 	/usr/bin/ld: cannot find -ljansson
> 
> Note: there is the same issue with examples/vm_power_manager/Makefile
> 
> I start thinking it is not reasonnable to enable an optional dependency
> in our Makefiles. Perhaps the only solution is to use meson...

Ouch, yes if x86_64 pkg-config finds the available library, and it is not available in its i686 form, or other combinations thereof, things become another-level of complex.

Indeed Meson is (good | getting better) at detecting these things, Bruce knows more IIRC :)

@Thomas, for this release I propose to rework to use CONFIG=N as default for Make, and allow packagers to enable if they wish. Is that a good next-step?

^ permalink raw reply	[flat|nested] 220+ messages in thread

* Re: [PATCH v9 03/12] telemetry: initial telemetry infrastructure
  2018-10-27  2:19                     ` Van Haaren, Harry
@ 2018-10-27  2:33                       ` Thomas Monjalon
  0 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-27  2:33 UTC (permalink / raw)
  To: Van Haaren, Harry
  Cc: Richardson, Bruce, dev, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Laatz, Kevin, Nicolau, Radu, Hunt, David

27/10/2018 04:19, Van Haaren, Harry:
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> > 27/10/2018 01:59, Harry van Haaren:
> > > --- a/mk/rte.vars.mk
> > > +++ b/mk/rte.vars.mk
> > > +JANSSON := $(shell pkg-config --exists jansson; echo $$?)
> > > +ifneq ($(JANSSON),0)
> > > +$(warning Jansson not found, disabling RTE_LIBRTE_TELEMETRY)
> > > +CONFIG_RTE_LIBRTE_TELEMETRY = n
> > > +endif
> > 
> > It fails for cross-compilation.
> > Example:
> > When compiling i686 on x86_64 host, no error with
> > 	pkg-config --exists jansson
> > but fails when linking:
> > 	/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-pc-linux-
> > gnu/8.2.1/../../../libjansson.so when searching for -ljansson
> > 	/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-pc-linux-
> > gnu/8.2.1/../../../libjansson.a when searching for -ljansson
> > 	/usr/bin/ld: skipping incompatible /usr/lib/libjansson.so when searching
> > for -ljansson
> > 	/usr/bin/ld: skipping incompatible /usr/lib/libjansson.a when searching
> > for -ljansson
> > 	/usr/bin/ld: cannot find -ljansson
> > 
> > Note: there is the same issue with examples/vm_power_manager/Makefile
> > 
> > I start thinking it is not reasonnable to enable an optional dependency
> > in our Makefiles. Perhaps the only solution is to use meson...
> 
> Ouch, yes if x86_64 pkg-config finds the available library, and it is not available in its i686 form, or other combinations thereof, things become another-level of complex.

On ArchLinux, I have pkg-config-32 for i686 libs.
And for true cross-compilation with sysroot, some environment variables
are required. See this doc:
	https://autotools.io/pkgconfig/cross-compiling.html

> Indeed Meson is (good | getting better) at detecting these things, Bruce knows more IIRC :)
> 
> @Thomas, for this release I propose to rework to use CONFIG=N as default for Make, and allow packagers to enable if they wish. Is that a good next-step?

Yes it looks reasonnable disabling telemetry by default in make case.

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v10 00/12] Introduce Telemetry Library
  2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
                                   ` (11 preceding siblings ...)
  2018-10-26 23:59                 ` [PATCH v9 12/12] build: add dependency on telemetry to apps in meson Harry van Haaren
@ 2018-10-27  9:17                 ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 01/12] eal: add option register infrastructure Harry van Haaren
                                     ` (12 more replies)
  12 siblings, 13 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Harry van Haaren

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

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

v5:
  - Moved the BUF_SIZE define to fix build (Harry)
  - Set default config for telemetry to 'n' (Harry)
  - Improved Doxygen comments (Thomas)
  - Cleaned up rte_param struct (Thomas)

v6:
  - Renamed rte_param to rte_option (Thomas)
  - Moved internal functions to eal_private.h (Gaetan)
  - Added fail check for pthread_attr_init() (Mattias)
  - Changed socket implementation to SOCK_SEQPACKET (Mattias)
  - Added check to avoid option duplicates (Gaetan)
  - Removed telemetry example from Doxygen comment (Gaetan)
  - General Doxygen clean-up (Thomas)
  - General code clean-up (Mattias)

v7:
  - Fix object leak (Mattias)
  - General clean-up (Mattias)
  - Fix rte_option header define
  - Added release note update
  - Rebased

v8:
  - Added missing experimental tags
  - Added Gaetan's Ack from ML

v9:
  - Added doxygen to build (Thomas)
  - Added doxygen @file tag (Thomas)
  - Detect jansson library dynamically with Make/pkg-config (Thomas)
  - Make jansson optional build-dep for Meson (Thomas/Bruce)

v10:
  - Location of Telemetry in doxygen (Thomas)
  - Linking jansson in patch of first usage (Thomas)
  - Meson detection in patch of first usage (Thomas)
  - Removed pkg-config check in Make due to issues with 32/64 bit (Thomas)
  - MAINTAINERS file entires introduced earlier and split (Thomas)
  - Disable Telemetry library in Make by default due to jansson dep (Thomas)

Ciara Power (9):
  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 ability to disable selftest
  doc: add telemetry documentation
  usertools: add client python script for telemetry

Kevin Laatz (3):
  eal: add option 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/api/doxy-api-index.md                     |    1 +
 doc/api/doxy-api.conf.in                      |    1 +
 doc/guides/howto/index.rst                    |    1 +
 doc/guides/howto/telemetry.rst                |   85 +
 doc/guides/rel_notes/release_18_11.rst        |    6 +
 lib/Makefile                                  |    2 +
 lib/librte_eal/bsdapp/eal/Makefile            |    1 +
 lib/librte_eal/bsdapp/eal/eal.c               |   16 +-
 lib/librte_eal/common/Makefile                |    1 +
 lib/librte_eal/common/eal_filesystem.h        |   15 +-
 lib/librte_eal/common/eal_private.h           |   21 +
 lib/librte_eal/common/include/rte_eal.h       |    9 +
 lib/librte_eal/common/include/rte_option.h    |   63 +
 lib/librte_eal/common/meson.build             |    2 +
 lib/librte_eal/common/rte_option.c            |   54 +
 lib/librte_eal/linuxapp/eal/Makefile          |    1 +
 lib/librte_eal/linuxapp/eal/eal.c             |   16 +-
 lib/librte_eal/rte_eal_version.map            |    2 +
 lib/librte_telemetry/Makefile                 |   30 +
 lib/librte_telemetry/meson.build              |   15 +
 lib/librte_telemetry/rte_telemetry.c          | 1815 +++++++++++++++++
 lib/librte_telemetry/rte_telemetry.h          |   66 +
 lib/librte_telemetry/rte_telemetry_internal.h |   81 +
 lib/librte_telemetry/rte_telemetry_parser.c   |  586 ++++++
 lib/librte_telemetry/rte_telemetry_parser.h   |   14 +
 .../rte_telemetry_parser_test.c               |  534 +++++
 .../rte_telemetry_parser_test.h               |   39 +
 .../rte_telemetry_socket_tests.h              |   36 +
 .../rte_telemetry_version.map                 |   10 +
 lib/meson.build                               |    3 +-
 meson.build                                   |    2 +
 mk/rte.app.mk                                 |    3 +-
 usertools/dpdk-telemetry-client.py            |  116 ++
 42 files changed, 3654 insertions(+), 20 deletions(-)
 create mode 100644 doc/guides/howto/telemetry.rst
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.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.17.1

^ permalink raw reply	[flat|nested] 220+ messages in thread

* [PATCH v10 01/12] eal: add option register infrastructure
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 02/12] eal: make get runtime dir function public Harry van Haaren
                                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Kevin Laatz

From: Kevin Laatz <kevin.laatz@intel.com>

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 <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>
---
 lib/librte_eal/bsdapp/eal/Makefile         |  1 +
 lib/librte_eal/bsdapp/eal/eal.c            | 14 ++++-
 lib/librte_eal/common/Makefile             |  1 +
 lib/librte_eal/common/eal_private.h        | 21 ++++++++
 lib/librte_eal/common/include/rte_option.h | 63 ++++++++++++++++++++++
 lib/librte_eal/common/meson.build          |  2 +
 lib/librte_eal/common/rte_option.c         | 54 +++++++++++++++++++
 lib/librte_eal/linuxapp/eal/Makefile       |  1 +
 lib/librte_eal/linuxapp/eal/eal.c          | 14 ++++-
 lib/librte_eal/rte_eal_version.map         |  1 +
 10 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_eal/common/include/rte_option.h
 create mode 100644 lib/librte_eal/common/rte_option.c

diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index d19f53c1e..bfeddaadc 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -67,6 +67,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_option.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 f94c5c5f4..11cbda964 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -791,6 +800,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index cca68826f..87d8c455d 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_option.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/eal_private.h b/lib/librte_eal/common/eal_private.h
index b189d675d..442c6dc48 100644
--- a/lib/librte_eal/common/eal_private.h
+++ b/lib/librte_eal/common/eal_private.h
@@ -349,4 +349,25 @@ dev_sigbus_handler_register(void);
 int
 dev_sigbus_handler_unregister(void);
 
+/**
+ * Check if the option is registered.
+ *
+ * @param option
+ *  The option to be parsed.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -1 on fail
+ */
+int
+rte_option_parse(const char *opt);
+
+/**
+ * Iterate through the registered options and execute the associated
+ * callback if enabled.
+ */
+void
+rte_option_init(void);
+
 #endif /* _EAL_PRIVATE_H_ */
diff --git a/lib/librte_eal/common/include/rte_option.h b/lib/librte_eal/common/include/rte_option.h
new file mode 100644
index 000000000..8957b970c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_option.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#ifndef __INCLUDE_RTE_OPTION_H__
+#define __INCLUDE_RTE_OPTION_H__
+
+/**
+ * @file
+ *
+ * This API offers the ability to register options to the EAL command line and
+ * map those options to functions that will be executed at the end of EAL
+ * initialization. These options will be available as part of the EAL command
+ * line of applications and are dynamically managed.
+ *
+ * This is used primarily by DPDK libraries offering command line options.
+ * Currently, this API is limited to registering options without argument.
+ *
+ * The register API can be used to resolve circular dependency issues
+ * between EAL and the library. The library uses EAL, but is also initialized
+ * by EAL. Hence, EAL depends on the init function of the library. The API
+ * introduced in rte_option allows us to register the library init with EAL
+ * (passing a function pointer) and avoid the circular dependency.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*rte_option_cb)(void);
+
+/*
+ * Structure describing the EAL command line option being registered.
+ */
+struct rte_option {
+	TAILQ_ENTRY(rte_option) next; /**< Next entry in the list. */
+	char *opt_str;             /**< The option name. */
+	rte_option_cb cb;          /**< Function called when option is used. */
+	int enabled;               /**< Set when the option is used. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register an option to the EAL command line.
+ * When recognized, the associated function will be executed at the end of EAL
+ * initialization.
+ *
+ * The associated structure must be available the whole time this option is
+ * registered (i.e. not stack memory).
+ *
+ * @param opt
+ *  Structure describing the option to parse.
+ */
+void __rte_experimental
+rte_option_register(struct rte_option *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 04c414356..2a10d57d8 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -34,6 +34,7 @@ common_sources = files(
 	'malloc_mp.c',
 	'rte_keepalive.c',
 	'rte_malloc.c',
+	'rte_option.c',
 	'rte_reciprocal.c',
 	'rte_service.c'
 )
@@ -71,6 +72,7 @@ common_headers = files(
 	'include/rte_malloc_heap.h',
 	'include/rte_memory.h',
 	'include/rte_memzone.h',
+	'include/rte_option.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_option.c b/lib/librte_eal/common/rte_option.c
new file mode 100644
index 000000000..02d59a869
--- /dev/null
+++ b/lib/librte_eal/common/rte_option.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_option.h>
+
+#include "eal_private.h"
+
+TAILQ_HEAD(rte_option_list, rte_option);
+
+struct rte_option_list rte_option_list =
+	TAILQ_HEAD_INITIALIZER(rte_option_list);
+
+static struct rte_option *option;
+
+int
+rte_option_parse(const char *opt)
+{
+	/* Check if the option is registered */
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt, option->opt_str) == 0) {
+			option->enabled = 1;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+void __rte_experimental
+rte_option_register(struct rte_option *opt)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (strcmp(opt->opt_str, option->opt_str) == 0)
+			RTE_LOG(INFO, EAL, "Option %s has already been registered.",
+					opt->opt_str);
+			return;
+	}
+
+	TAILQ_INSERT_HEAD(&rte_option_list, opt, next);
+}
+
+void
+rte_option_init(void)
+{
+	TAILQ_FOREACH(option, &rte_option_list, next) {
+		if (option->enabled)
+			option->cb();
+	}
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 728088594..51deb5797 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -75,6 +75,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_option.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 76536bae2..930070e43 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_option.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_option_parse(argv[optind-1]);
+			if (ret == 0)
+				continue;
+
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
@@ -1080,6 +1089,9 @@ rte_eal_init(int argc, char **argv)
 
 	rte_eal_mcfg_complete();
 
+	/* Call each registered callback, if enabled */
+	rte_option_init();
+
 	return fctret;
 }
 
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index f4a2b38fd..5b6d91ffd 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -350,6 +350,7 @@ EXPERIMENTAL {
 	rte_mp_request_sync;
 	rte_mp_request_async;
 	rte_mp_sendmsg;
+	rte_option_register;
 	rte_service_lcore_attr_get;
 	rte_service_lcore_attr_reset_all;
 	rte_service_may_be_active;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 02/12] eal: make get runtime dir function public
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 01/12] eal: add option register infrastructure Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
                                     ` (10 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Kevin Laatz

From: Kevin Laatz <kevin.laatz@intel.com>

This patch makes the eal_get_runtime_dir() API public so it can be used
from outside EAL.

Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_eal/bsdapp/eal/eal.c         |  2 +-
 lib/librte_eal/common/eal_filesystem.h  | 15 ++++++++-------
 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, 20 insertions(+), 9 deletions(-)

diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 11cbda964..21997ced8 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 de05febf4..b3e8ae5ea 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,8 @@ 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 +67,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 +79,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 +100,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 6514a9fe6..a0cedd573 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -507,6 +507,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 930070e43..7b11375f4 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 5b6d91ffd..28bfa4950 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -259,6 +259,7 @@ DPDK_18.08 {
 DPDK_18.11 {
 	global:
 
+	rte_eal_get_runtime_dir;
 	rte_eal_hotplug_add;
 	rte_eal_hotplug_remove;
 	rte_strscpy;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 03/12] telemetry: initial telemetry infrastructure
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 01/12] eal: add option register infrastructure Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 02/12] eal: make get runtime dir function public Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 04/12] telemetry: add initial connection socket Harry van Haaren
                                     ` (9 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz,
	Radu Nicolau

From: Ciara Power <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 option. 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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

---

v10:
 - change position of Telemetry in index Doxygen index (Thomas)
 - Add MAINTAINER file entry for lib/telemetry files (Thomas)
 - Removed -ljansson from mk/rte.app.mk (Thomas)
 - Removed pkg-config jansson check in mk/rte.vars.mk (Thomas)
 - Set CONFIG_TELEMETRY=N by default (Thomas)
---
 MAINTAINERS                                   |   3 +
 config/common_base                            |   5 +
 doc/api/doxy-api-index.md                     |   1 +
 doc/api/doxy-api.conf.in                      |   1 +
 lib/Makefile                                  |   2 +
 lib/librte_telemetry/Makefile                 |  27 ++++
 lib/librte_telemetry/meson.build              |   7 +
 lib/librte_telemetry/rte_telemetry.c          | 123 ++++++++++++++++++
 lib/librte_telemetry/rte_telemetry.h          |  51 ++++++++
 lib/librte_telemetry/rte_telemetry_internal.h |  32 +++++
 .../rte_telemetry_version.map                 |   8 ++
 lib/meson.build                               |   2 +-
 mk/rte.app.mk                                 |   3 +-
 13 files changed, 263 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/MAINTAINERS b/MAINTAINERS
index 17ee5b9d3..a50214d7a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1208,6 +1208,9 @@ F: test/bpf/
 F: test/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Telemetry - EXPERIMENTAL
+M: Kevin Laatz <kevin.laatz@intel.com>
+F: lib/librte_telemetry/
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index 38beaabb3..4ba8f651d 100644
--- a/config/common_base
+++ b/config/common_base
@@ -756,6 +756,11 @@ CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
 CONFIG_RTE_LIBRTE_HASH=y
 CONFIG_RTE_LIBRTE_HASH_DEBUG=n
 
+#
+# Compile librte_telemetry
+#
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+
 #
 # Compile librte_efd
 #
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index a3039d168..cd2b7e413 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -29,6 +29,7 @@ The public API headers are grouped by topics:
   [metrics]            (@ref rte_metrics.h),
   [bitrate]            (@ref rte_bitrate.h),
   [latency]            (@ref rte_latencystats.h),
+  [telemetry]          (@ref rte_telemetry.h),
   [devargs]            (@ref rte_devargs.h),
   [PCI]                (@ref rte_pci.h),
   [vfio]               (@ref rte_vfio.h)
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 3b652ac9c..77ba327a8 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -56,6 +56,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_sched \
                           @TOPDIR@/lib/librte_security \
                           @TOPDIR@/lib/librte_table \
+                          @TOPDIR@/lib/librte_telemetry \
                           @TOPDIR@/lib/librte_timer \
                           @TOPDIR@/lib/librte_vhost
 INPUT                   += @API_EXAMPLES@
diff --git a/lib/Makefile b/lib/Makefile
index 062cbdfd7..b7370ef97 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -106,6 +106,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 000000000..a2d4ff166
--- /dev/null
+++ b/lib/librte_telemetry/Makefile
@@ -0,0 +1,27 @@
+# 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
+
+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 000000000..7716076a9
--- /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 000000000..7f4ad0342
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -0,0 +1,123 @@
+/* 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_option.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_experimental
+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);
+
+	ret = pthread_attr_init(&attr);
+	if (ret != 0) {
+		TELEMETRY_LOG_ERR("Pthread attribute init failed");
+		return -EPERM;
+	}
+
+	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_experimental
+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_option option = {
+	.opt_str = "--telemetry",
+	.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_option_register(&option);
+}
diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h
new file mode 100644
index 000000000..97674ae2d
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdint.h>
+
+#ifndef _RTE_TELEMETRY_H_
+#define _RTE_TELEMETRY_H_
+
+/**
+ * @file
+ * RTE Telemetry
+ *
+ * The telemetry library provides a method to retrieve statistics from
+ * DPDK by sending a JSON encoded message over a socket. DPDK will send
+ * a JSON encoded response containing telemetry data.
+ ***/
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 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_experimental
+rte_telemetry_init(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clean up and free memory.
+ *
+ * @return
+ *  0 on success
+ * @return
+ *  -EPERM on failure
+ */
+int32_t __rte_experimental
+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 000000000..4e810a84c
--- /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 000000000..bbcd9a796
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -0,0 +1,8 @@
+EXPERIMENTAL {
+	global:
+
+	rte_telemetry_cleanup;
+	rte_telemetry_init;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 2b903fa37..9d1f353d2 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -25,7 +25,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 c0036daf8..a14e83c71 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -51,7 +51,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += --whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += --no-whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_JOBSTATS)       += -lrte_jobstats
-_LDLIBS-$(CONFIG_RTE_LIBRTE_METRICS)        += -lrte_metrics
 _LDLIBS-$(CONFIG_RTE_LIBRTE_BITRATE)        += -lrte_bitratestats
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS)  += -lrte_latencystats
 _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
@@ -80,6 +79,8 @@ _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_METRICS)        += -lrte_metrics
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += -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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 04/12] telemetry: add initial connection socket
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (2 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 05/12] telemetry: add client feature and sockets Harry van Haaren
                                     ` (8 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 7f4ad0342..e9b3330ca 100644
--- a/lib/librte_telemetry/rte_telemetry.c
+++ b/lib/librte_telemetry/rte_telemetry.c
@@ -3,23 +3,163 @@
  */
 
 #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_option.h>
+#include <rte_string_fns.h>
 
 #include "rte_telemetry.h"
 #include "rte_telemetry_internal.h"
 
+#define BUF_SIZE 1024
 #define SLEEP_TIME 10
 
 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) {
+		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 +167,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 +196,67 @@ 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;
+	char socket_path[BUF_SIZE];
+
+	if (telemetry == NULL)
+		return -1;
+
+	telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (telemetry->server_fd == -1) {
+		TELEMETRY_LOG_ERR("Failed to open socket");
+		return -1;
+	}
+
+	ret  = rte_telemetry_set_socket_nonblock(telemetry->server_fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		goto close_socket;
+	}
+
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+	unlink(socket_path);
+
+	if (bind(telemetry->server_fd, (struct sockaddr *)&addr,
+		sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Socket binding error");
+		goto close_socket;
+	}
+
+	return 0;
+
+close_socket:
+	if (close(telemetry->server_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		return -EPERM;
+	}
+
+	return -1;
+}
+
 int32_t __rte_experimental
 rte_telemetry_init()
 {
@@ -77,6 +284,14 @@ rte_telemetry_init()
 		return -EPERM;
 	}
 
+	ret = rte_telemetry_create_socket(static_telemetry);
+	if (ret < 0) {
+		ret = rte_telemetry_cleanup();
+		if (ret < 0)
+			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
+		return -EPERM;
+	}
+
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
 		(void *)static_telemetry);
@@ -95,11 +310,21 @@ rte_telemetry_init()
 int32_t __rte_experimental
 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 4e810a84c..569d56ab8 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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 05/12] telemetry: add client feature and sockets
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (3 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 04/12] telemetry: add initial connection socket Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 06/12] telemetry: add parser for client socket messages Harry van Haaren
                                     ` (7 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

---

v10:
 - remove error prone pkg-config detection (Thomas)
 - Re-add -ljansson to mk/rte.app.mk, was in patch 1 (Thomas)
---
 lib/librte_telemetry/Makefile                 |   1 +
 lib/librte_telemetry/meson.build              |   7 +
 lib/librte_telemetry/rte_telemetry.c          | 370 +++++++++++++++++-
 lib/librte_telemetry/rte_telemetry_internal.h |  25 ++
 mk/rte.app.mk                                 |   2 +-
 5 files changed, 401 insertions(+), 4 deletions(-)

diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile
index a2d4ff166..0d61361f4 100644
--- a/lib/librte_telemetry/Makefile
+++ b/lib/librte_telemetry/Makefile
@@ -13,6 +13,7 @@ CFLAGS += -DALLOW_EXPERIMENTAL_API
 LDLIBS += -lrte_eal -lrte_ethdev
 LDLIBS += -lrte_metrics
 LDLIBS += -lpthread
+LDLIBS += -ljansson
 
 EXPORT_MAP := rte_telemetry_version.map
 
diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index 7716076a9..7b939805e 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -5,3 +5,10 @@ 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: false)
+if jansson.found()
+	ext_deps += jansson
+else
+	build = false
+endif
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index e9b3330ca..3c8b922f5 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>
@@ -18,6 +19,7 @@
 #include "rte_telemetry_internal.h"
 
 #define BUF_SIZE 1024
+#define ACTION_POST 1
 #define SLEEP_TIME 10
 
 static telemetry_impl *static_telemetry;
@@ -39,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;
 }
 
@@ -115,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 +216,38 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry)
 	return 0;
 }
 
+static int32_t
+rte_telemetry_read_client(struct telemetry_impl *telemetry)
+{
+	char buf[BUF_SIZE];
+	int ret, buffer_read;
+
+	buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	} else if (buffer_read == 0) {
+		goto close_socket;
+	} else {
+		buf[buffer_read] = '\0';
+		ret = rte_telemetry_parse_client_message(telemetry, buf);
+		if (ret < 0)
+			TELEMETRY_LOG_WARN("Parse message failed");
+		goto close_socket;
+	}
+
+close_socket:
+	if (close(telemetry->accept_fd) < 0) {
+		TELEMETRY_LOG_ERR("Close TELEMETRY socket failed");
+		free(telemetry);
+		return -EPERM;
+	}
+	telemetry->accept_fd = 0;
+
+	return 0;
+}
+
 static int32_t
 rte_telemetry_accept_new_client(struct telemetry_impl *telemetry)
 {
@@ -141,8 +259,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);
@@ -151,6 +269,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);
+
+		if (bytes > 0) {
+			client_buf[bytes] = '\0';
+			telemetry->request_client = client;
+		}
 	}
 
 	return 0;
@@ -173,6 +316,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;
 }
 
@@ -291,6 +440,7 @@ rte_telemetry_init()
 			TELEMETRY_LOG_ERR("TELEMETRY cleanup failed");
 		return -EPERM;
 	}
+	TAILQ_INIT(&static_telemetry->client_list_head);
 
 	ret = rte_ctrl_thread_create(&static_telemetry->thread_id,
 		telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func,
@@ -307,11 +457,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_experimental
 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) {
@@ -328,6 +506,192 @@ 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;
+
+	if (telemetry == NULL) {
+		TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API");
+		return -ENODEV;
+	}
+
+	if (client_path == NULL) {
+		TELEMETRY_LOG_ERR("Invalid client path");
+		return -EINVAL;
+	}
+
+	telemetry_client *client;
+	TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) {
+		if (strcmp(client_path, client->file_path) == 0) {
+			TELEMETRY_LOG_WARN("'%s' already registered",
+					client_path);
+			return -EINVAL;
+		}
+	}
+
+	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd == -1) {
+		TELEMETRY_LOG_ERR("Client socket error");
+		return -EACCES;
+	}
+
+	ret = rte_telemetry_set_socket_nonblock(fd);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK");
+		return -EPERM;
+	}
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	telemetry_client *new_client = malloc(sizeof(telemetry_client));
+	new_client->file_path = strdup(client_path);
+	new_client->fd = fd;
+
+	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
+		TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work",
+				client_path);
+		ret = rte_telemetry_client_cleanup(new_client);
+		if (ret < 0) {
+			TELEMETRY_LOG_ERR("Client cleanup failed");
+			return -EPERM;
+		}
+		return -EINVAL;
+	}
+
+	TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list);
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf)
+{
+	int ret, action_int;
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+
+	if (root == NULL) {
+		TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
+				error.text);
+		goto fail;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
+		goto fail;
+	}
+
+	json_t *action = json_object_get(root, "action");
+	if (action == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have action field");
+		goto fail;
+	} else if (!json_is_integer(action)) {
+		TELEMETRY_LOG_WARN("Action value is not an integer");
+		goto fail;
+	}
+
+	json_t *command = json_object_get(root, "command");
+	if (command == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have command field");
+		goto fail;
+	} else if (!json_is_string(command)) {
+		TELEMETRY_LOG_WARN("Command value is not a string");
+		goto fail;
+	}
+
+	action_int = json_integer_value(action);
+	if (action_int != ACTION_POST) {
+		TELEMETRY_LOG_WARN("Invalid action code");
+		goto fail;
+	}
+
+	if (strcmp(json_string_value(command), "clients") != 0) {
+		TELEMETRY_LOG_WARN("Invalid command");
+		goto fail;
+	}
+
+	json_t *data = json_object_get(root, "data");
+	if (data == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have data field");
+		goto fail;
+	}
+
+	json_t *client_path = json_object_get(data, "client_path");
+	if (client_path == NULL) {
+		TELEMETRY_LOG_WARN("Request does not have client_path field");
+		goto fail;
+	}
+
+	if (!json_is_string(client_path)) {
+		TELEMETRY_LOG_WARN("Client_path value is not a string");
+		goto fail;
+	}
+
+	ret = rte_telemetry_register_client(telemetry,
+			json_string_value(client_path));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not register client");
+		telemetry->register_fail_count++;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	TELEMETRY_LOG_WARN("Client attempted to register with invalid message");
+	json_decref(root);
+	return -1;
+}
+
 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 569d56ab8..e3292cf40 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 a14e83c71..c5aaa9da5 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,7 @@ _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_METRICS)        += -lrte_metrics
-_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += -lrte_telemetry
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += -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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 06/12] telemetry: add parser for client socket messages
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (4 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 05/12] telemetry: add client feature and sockets Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 07/12] telemetry: update metrics before sending stats Harry van Haaren
                                     ` (6 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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   |  14 +
 .../rte_telemetry_version.map                 |   1 +
 7 files changed, 608 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 0d61361f4..95c72963a 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 7b939805e..e459d0b80 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'
 
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 3c8b922f5..cac788438 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
@@ -283,6 +284,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,6 +295,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry)
 		if (bytes > 0) {
 			client_buf[bytes] = '\0';
 			telemetry->request_client = client;
+			ret = rte_telemetry_parse(telemetry, client_buf);
+			if (ret < 0) {
+				TELEMETRY_LOG_WARN("Parse socket input failed: %i",
+						ret);
+				return -1;
+			}
 		}
 	}
 
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index e3292cf40..86a5ba15e 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 000000000..556abbe4a
--- /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_experimental
+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 000000000..b7051945b
--- /dev/null
+++ b/lib/librte_telemetry/rte_telemetry_parser.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include "rte_telemetry_internal.h"
+#include "rte_compat.h"
+
+#ifndef _RTE_TELEMETRY_PARSER_H_
+#define _RTE_TELEMETRY_PARSER_H_
+
+int32_t __rte_experimental
+rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data);
+
+#endif
diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map
index bbcd9a796..fb0b5be62 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	rte_telemetry_cleanup;
 	rte_telemetry_init;
+	rte_telemetry_parse;
 
 	local: *;
 };
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 07/12] telemetry: update metrics before sending stats
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (5 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 06/12] telemetry: add parser for client socket messages Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 08/12] telemetry: format json response when " Harry van Haaren
                                     ` (5 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 cac788438..7616e7c91 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 86a5ba15e..0082cb2ca 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 556abbe4a..03a58a2fd 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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 08/12] telemetry: format json response when sending stats
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (6 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 07/12] telemetry: update metrics before sending stats Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 09/12] telemetry: add ability to disable selftest Harry van Haaren
                                     ` (4 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 lib/librte_telemetry/Makefile                 |   1 +
 lib/librte_telemetry/meson.build              |   4 +-
 lib/librte_telemetry/rte_telemetry.c          | 962 +++++++++++++++++-
 lib/librte_telemetry/rte_telemetry.h          |  15 +
 lib/librte_telemetry/rte_telemetry_internal.h |   3 +
 .../rte_telemetry_parser_test.c               | 534 ++++++++++
 .../rte_telemetry_parser_test.h               |  39 +
 .../rte_telemetry_socket_tests.h              |  36 +
 .../rte_telemetry_version.map                 |   1 +
 9 files changed, 1591 insertions(+), 4 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 95c72963a..1a0506913 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 e459d0b80..b3bbf5bd0 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'
 
diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c
index 7616e7c91..5b66b71c4 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)
 {
@@ -190,7 +209,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 +221,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 +558,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");
@@ -335,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);
@@ -347,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;
 }
@@ -834,6 +1171,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_SEQPACKET, 0);
+	struct sockaddr_un addr = {0};
+
+	if (sockfd < 0) {
+		TELEMETRY_LOG_ERR("Test socket creation failure");
+		return -1;
+	}
+
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path));
+	unlink(valid_client_path);
+
+	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		TELEMETRY_LOG_ERR("Test socket binding failure");
+		return -1;
+	}
+
+	if (listen(sockfd, 1) < 0) {
+		TELEMETRY_LOG_ERR("Listen failure");
+		return -1;
+	}
+
+	return sockfd;
+}
+
+int32_t __rte_experimental
+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_SEQPACKET, 0);
+	*recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+
+	listen(telemetry->server_fd, 5);
+	addr.sun_family = AF_UNIX;
+	rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path));
+	strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
+
+	ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not connect socket");
+		return -1;
+	}
+
+	telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL);
+
+	addrs.sun_family = AF_UNIX;
+	strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path));
+	unlink(client_path);
+
+	ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs));
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not bind socket");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int32_t
+rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct)
+{
+	json_error_t error;
+	json_t *root = json_loads(buf, 0, &error);
+	int arraylen, i;
+	json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj,
+	       *statsArrayObj;
+
+	stats = NULL;
+	port = NULL;
+	name = NULL;
+
+	if (buf == NULL) {
+		TELEMETRY_LOG_ERR("JSON message is NULL");
+		return -EINVAL;
+	}
+
+	if (root == NULL) {
+		TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s",
+				error.text);
+		return -EPERM;
+	} else if (!json_is_object(root)) {
+		TELEMETRY_LOG_ERR("JSON Request is not a JSON object");
+		json_decref(root);
+		return -EINVAL;
+	}
+
+	status = json_object_get(root, "status_code");
+	if (!status) {
+		TELEMETRY_LOG_ERR("Request does not have status field");
+		return -EINVAL;
+	} else if (!json_is_string(status)) {
+		TELEMETRY_LOG_ERR("Status value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->status_code = strdup(json_string_value(status));
+
+	dataArray = json_object_get(root, "data");
+	if (dataArray == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have data field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(dataArray);
+	if (arraylen == 0) {
+		json_data_struct->data = "null";
+		return -EINVAL;
+	}
+
+	for (i = 0; i < arraylen; i++) {
+		dataArrayObj = json_array_get(dataArray, i);
+		port = json_object_get(dataArrayObj, "port");
+		stats = json_object_get(dataArrayObj, "stats");
+	}
+
+	if (port == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have port field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(port)) {
+		TELEMETRY_LOG_ERR("Port value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->port = json_integer_value(port);
+
+	if (stats == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have stats field");
+		return -EINVAL;
+	}
+
+	arraylen = json_array_size(stats);
+	for (i = 0; i < arraylen; i++) {
+		statsArrayObj = json_array_get(stats, i);
+		name = json_object_get(statsArrayObj, "name");
+		value = json_object_get(statsArrayObj, "value");
+	}
+
+	if (name == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have name field");
+		return -EINVAL;
+	}
+
+	if (!json_is_string(name)) {
+		TELEMETRY_LOG_ERR("Stat name value is not a string");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_name = strdup(json_string_value(name));
+
+	if (value == NULL) {
+		TELEMETRY_LOG_ERR("Request does not have value field");
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(value)) {
+		TELEMETRY_LOG_ERR("Stat value is not an integer");
+		return -EINVAL;
+	}
+
+	json_data_struct->stat_value = json_integer_value(value);
+
+	return 0;
+}
+
+static void
+rte_telemetry_free_test_data(struct json_data *data)
+{
+	free(data->status_code);
+	free(data->stat_name);
+	free(data);
+}
+
+int32_t
+rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	int port = 0;
+	int value = 0;
+	int fail_count = 0;
+	int buffer_read = 0;
+	char buf[BUF_SIZE];
+	struct json_data *data_struct;
+	errno = 0;
+	const char *status = "Status OK: 200";
+	const char *name = "rx_good_packets";
+	const char *valid_json_message = "{\"action\":0,\"command\":"
+	"\"ports_stats_values_by_name\",\"data\":{\"ports\""
+	":[0],\"stats\":[\"rx_good_packets\"]}}";
+
+	ret = send(fd, valid_json_message, strlen(valid_json_message), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not parse stats");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->port != port) {
+		TELEMETRY_LOG_ERR("Port is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->stat_name, name) != 0) {
+		TELEMETRY_LOG_ERR("Stat name is invalid");
+		fail_count++;
+	}
+
+	if (data_struct->stat_value != value) {
+		TELEMETRY_LOG_ERR("Stat value is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	const char *invalid_json = "{]";
+	const char *status = "Status Error: Unknown";
+	const char *data = "null";
+	struct json_data *data_struct;
+	int buffer_read = 0;
+	errno = 0;
+
+	ret = send(fd, invalid_json, strlen(invalid_json), 0);
+	if (ret < 0) {
+		TELEMETRY_LOG_ERR("Could not send message over socket");
+		return -1;
+	}
+
+	rte_telemetry_run(telemetry);
+	buffer_read = recv(fd, buf, BUF_SIZE-1, 0);
+
+	if (buffer_read == -1) {
+		TELEMETRY_LOG_ERR("Read error");
+		return -1;
+	}
+
+	buf[buffer_read] = '\0';
+
+	data_struct = calloc(1, sizeof(struct json_data));
+	ret = rte_telemetry_stat_parse(buf, data_struct);
+
+	if (ret < 0)
+		TELEMETRY_LOG_ERR("Could not parse stats");
+
+	if (strcmp(data_struct->status_code, status) != 0) {
+		TELEMETRY_LOG_ERR("Status code is invalid");
+		fail_count++;
+	}
+
+	if (strcmp(data_struct->data, data) != 0) {
+		TELEMETRY_LOG_ERR("Data status is invalid");
+		fail_count++;
+	}
+
+	rte_telemetry_free_test_data(data_struct);
+	if (fail_count > 0)
+		return -1;
+
+	TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test");
+
+	return 0;
+}
+
+int32_t
+rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd)
+{
+	int ret;
+	char buf[BUF_SIZE];
+	int fail_count = 0;
+	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 97674ae2d..119db16fe 100644
--- a/lib/librte_telemetry/rte_telemetry.h
+++ b/lib/librte_telemetry/rte_telemetry.h
@@ -48,4 +48,19 @@ rte_telemetry_init(void);
 int32_t __rte_experimental
 rte_telemetry_cleanup(void);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 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_experimental
+rte_telemetry_selftest(void);
+
 #endif
diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h
index 0082cb2ca..de7afda30 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 000000000..5fe93fa6e
--- /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_SEQPACKET, 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 000000000..6ada85276
--- /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 000000000..db9167c5d
--- /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 fb0b5be62..fa62d7718 100644
--- a/lib/librte_telemetry/rte_telemetry_version.map
+++ b/lib/librte_telemetry/rte_telemetry_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_telemetry_cleanup;
 	rte_telemetry_init;
 	rte_telemetry_parse;
+	rte_telemetry_selftest;
 
 	local: *;
 };
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 09/12] telemetry: add ability to disable selftest
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (7 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 08/12] telemetry: format json response when " Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 10/12] doc: add telemetry documentation Harry van Haaren
                                     ` (3 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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 5b66b71c4..2e27491b5 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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 10/12] doc: add telemetry documentation
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (8 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 09/12] telemetry: add ability to disable selftest Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 11/12] usertools: add client python script for telemetry Harry van Haaren
                                     ` (2 subsequent siblings)
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 and a release notes update for
telemetry.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>
Acked-by: Marko Kovacevic <marko.kovacevic@intel.com>

---

v10:
 - Add MAINTAINERS entry for documentation (Thomas)
---
 MAINTAINERS                            |  1 +
 doc/guides/howto/index.rst             |  1 +
 doc/guides/howto/telemetry.rst         | 85 ++++++++++++++++++++++++++
 doc/guides/rel_notes/release_18_11.rst |  6 ++
 4 files changed, 93 insertions(+)
 create mode 100644 doc/guides/howto/telemetry.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index a50214d7a..86712b139 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1211,6 +1211,7 @@ F: doc/guides/prog_guide/bpf_lib.rst
 Telemetry - EXPERIMENTAL
 M: Kevin Laatz <kevin.laatz@intel.com>
 F: lib/librte_telemetry/
+F: doc/guides/howto/telemetry.rst
 
 Test Applications
 -----------------
diff --git a/doc/guides/howto/index.rst b/doc/guides/howto/index.rst
index e13a090c3..a642a2be1 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 000000000..3fcb0619e
--- /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.
diff --git a/doc/guides/rel_notes/release_18_11.rst b/doc/guides/rel_notes/release_18_11.rst
index c6256939b..ed7994241 100644
--- a/doc/guides/rel_notes/release_18_11.rst
+++ b/doc/guides/rel_notes/release_18_11.rst
@@ -256,6 +256,12 @@ New Features
   the specified port. The port must be stopped before the command call in order
   to reconfigure queues.
 
+* **Added Telemetry API.**
+
+  Added the telemetry API which allows applications to transparently expose
+  their telemetry via a UNIX socket in JSON. The JSON can be consumed by any
+  Service Assurance agent, such as CollectD.
+
 * **Add a new sample for vDPA**
 
   The vdpa sample application creates vhost-user sockets by using the
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 11/12] usertools: add client python script for telemetry
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (9 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 10/12] doc: add telemetry documentation Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27  9:17                   ` [PATCH v10 12/12] build: add dependency on telemetry to apps in meson Harry van Haaren
  2018-10-27 13:24                   ` [PATCH v10 00/12] Introduce Telemetry Library Thomas Monjalon
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Ciara Power, Brian Archbold, Kevin Laatz

From: Ciara Power <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 <ciara.power@intel.com>
Signed-off-by: Brian Archbold <brian.archbold@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@intel.com>

---

v10:
 - Add MAINTAINER entry for client script (Thomas)
---
 MAINTAINERS                        |   1 +
 usertools/dpdk-telemetry-client.py | 116 +++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+)
 create mode 100644 usertools/dpdk-telemetry-client.py

diff --git a/MAINTAINERS b/MAINTAINERS
index 86712b139..a919ffd45 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1212,6 +1212,7 @@ Telemetry - EXPERIMENTAL
 M: Kevin Laatz <kevin.laatz@intel.com>
 F: lib/librte_telemetry/
 F: doc/guides/howto/telemetry.rst
+F: usertools/dpdk-telemetry-client.py
 
 Test Applications
 -----------------
diff --git a/usertools/dpdk-telemetry-client.py b/usertools/dpdk-telemetry-client.py
new file mode 100644
index 000000000..6dcf62bac
--- /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_SEQPACKET)
+        self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+        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.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* [PATCH v10 12/12] build: add dependency on telemetry to apps in meson
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (10 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 11/12] usertools: add client python script for telemetry Harry van Haaren
@ 2018-10-27  9:17                   ` Harry van Haaren
  2018-10-27 13:24                   ` [PATCH v10 00/12] Introduce Telemetry Library Thomas Monjalon
  12 siblings, 0 replies; 220+ messages in thread
From: Harry van Haaren @ 2018-10-27  9:17 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, stephen, gaetan.rivet, shreyansh.jain,
	mattias.ronnblom, Kevin Laatz, Radu Nicolau

From: Kevin Laatz <kevin.laatz@intel.com>

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 <bruce.richardson@intel.com>
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Signed-off-by: Radu Nicolau <radu.nicolau@intel.com>
Acked-by: Harry van Haaren <harry.van.haaren@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/librte_telemetry/meson.build | 1 +
 lib/meson.build                  | 1 +
 meson.build                      | 2 ++
 10 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/app/meson.build b/app/meson.build
index e68d949e9..a9a026bbf 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -29,7 +29,7 @@ foreach app:apps
 	# use "deps" for internal DPDK dependencies, and "ext_deps" for
 	# external package/library requirements
 	ext_deps = []
-	deps = []
+	deps = dpdk_app_link_libraries
 
 	subdir(name)
 
@@ -43,7 +43,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 988cb4eb2..116c27f02 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 9c148e36e..a52b2ee4a 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 653907ded..eb8cc0499 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 eacd7a0f5..d735b186f 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 cd66618e9..6006c60f9 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -24,7 +24,7 @@ sources = files('cmdline.c',
 	'txonly.c',
 	'util.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 6f9228c87..275f00b60 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/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build
index b3bbf5bd0..9492f544e 100644
--- a/lib/librte_telemetry/meson.build
+++ b/lib/librte_telemetry/meson.build
@@ -9,6 +9,7 @@ cflags += '-DALLOW_EXPERIMENTAL_API'
 jansson = cc.find_library('jansson', required: false)
 if jansson.found()
 	ext_deps += jansson
+	dpdk_app_link_libraries += ['telemetry']
 else
 	build = false
 endif
diff --git a/lib/meson.build b/lib/meson.build
index 9d1f353d2..c0cc2d86e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -132,6 +132,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 c9af33532..b1e6eab6a 100644
--- a/meson.build
+++ b/meson.build
@@ -12,8 +12,10 @@ project('DPDK', 'C',
 cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
+dpdk_static_libraries = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
+dpdk_app_link_libraries = []
 
 driver_install_path = join_paths(get_option('libdir'), 'dpdk/drivers')
 eal_pmd_path = join_paths(get_option('prefix'), driver_install_path)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 220+ messages in thread

* Re: [PATCH v10 00/12] Introduce Telemetry Library
  2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
                                     ` (11 preceding siblings ...)
  2018-10-27  9:17                   ` [PATCH v10 12/12] build: add dependency on telemetry to apps in meson Harry van Haaren
@ 2018-10-27 13:24                   ` Thomas Monjalon
  12 siblings, 0 replies; 220+ messages in thread
From: Thomas Monjalon @ 2018-10-27 13:24 UTC (permalink / raw)
  To: Harry van Haaren, bruce.richardson, kevin.laatz
  Cc: dev, stephen, gaetan.rivet, shreyansh.jain, mattias.ronnblom,
	radu.nicolau

27/10/2018 11:17, Harry van Haaren:
> Ciara Power (9):
>   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 ability to disable selftest
>   doc: add telemetry documentation
>   usertools: add client python script for telemetry
> 
> Kevin Laatz (3):
>   eal: add option register infrastructure
>   eal: make get runtime dir function public
>   build: add dependency on telemetry to apps in meson

Applied with some changes as discussed on IRC with Harry.

Everybody spent so many time on this feature, I guess nobody wants to 
hear about it for some time :) Let's hope all will work as expected!

^ permalink raw reply	[flat|nested] 220+ messages in thread

end of thread, other threads:[~2018-10-27 13:24 UTC | newest]

Thread overview: 220+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-08-23 12:08 [PATCH 00/11] introduce telemetry library Ciara Power
2018-08-23 12:08 ` [PATCH 01/11] telemetry: initial telemetry infrastructure Ciara Power
2018-08-23 23:17   ` Stephen Hemminger
2018-08-23 23:17   ` Stephen Hemminger
2018-08-23 23:18   ` Stephen Hemminger
2018-08-23 23:19   ` Stephen Hemminger
2018-08-23 23:22   ` Stephen Hemminger
2018-08-28 17:12     ` Van Haaren, Harry
2018-08-24 13:03   ` Shreyansh Jain
2018-08-28 16:50     ` Van Haaren, Harry
2018-08-28 11:46   ` Gaëtan Rivet
2018-08-28 16:54     ` Van Haaren, Harry
2018-08-29  8:23       ` Gaëtan Rivet
2018-08-23 12:08 ` [PATCH 02/11] telemetry: add initial connection socket Ciara Power
2018-08-28 16:40   ` Gaëtan Rivet
2018-08-28 17:03     ` Van Haaren, Harry
2018-09-07  9:48   ` Burakov, Anatoly
2018-08-23 12:08 ` [PATCH 03/11] telemetry: add client feature and sockets Ciara Power
2018-08-23 23:27   ` Stephen Hemminger
2018-08-28 15:26     ` Hunt, David
2018-08-28 17:09       ` Van Haaren, Harry
2018-08-23 12:08 ` [PATCH 04/11] telemetry: add parser for client socket messages Ciara Power
2018-08-30 23:57   ` Gaëtan Rivet
2018-08-23 12:08 ` [PATCH 05/11] telemetry: update metrics before sending stats Ciara Power
2018-08-23 12:08 ` [PATCH 06/11] telemetry: format json response when " Ciara Power
2018-08-23 12:08 ` [PATCH 07/11] telemetry: add tests for telemetry api Ciara Power
2018-08-23 23:15   ` Stephen Hemminger
2018-08-23 12:08 ` [PATCH 08/11] telemetry: add vdev kvargs for selftest Ciara Power
2018-08-23 12:08 ` [PATCH 09/11] doc: add telemetry documentation Ciara Power
2018-09-25  8:53   ` Kovacevic, Marko
2018-08-23 12:08 ` [PATCH 10/11] usertools: add client python script for telemetry Ciara Power
2018-08-23 12:08 ` [PATCH 11/11] telemetry: add collectd plugin patch Ciara Power
2018-09-18  9:52   ` Thomas Monjalon
2018-09-19 11:09     ` Laatz, Kevin
2018-10-03 17:36 ` [PATCH v2 00/10] introduce telemetry library Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 01/10] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-04 14:13     ` Gaëtan Rivet
2018-10-03 17:36   ` [PATCH v2 02/10] telemetry: add initial connection socket Kevin Laatz
2018-10-03 18:40     ` Mattias Rönnblom
2018-10-03 19:36       ` Thomas Monjalon
2018-10-03 19:49         ` Mattias Rönnblom
2018-10-03 17:36   ` [PATCH v2 03/10] telemetry: add client feature and sockets Kevin Laatz
2018-10-03 19:06     ` Mattias Rönnblom
2018-10-03 17:36   ` [PATCH v2 04/10] telemetry: add parser for client socket messages Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 05/10] telemetry: update metrics before sending stats Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 06/10] telemetry: format json response when " Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 07/10] telemetry: add tests for telemetry api Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 08/10] telemetry: add ability to disable selftest Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 09/10] doc: add telemetry documentation Kevin Laatz
2018-10-03 17:36   ` [PATCH v2 10/10] usertools: add client python script for telemetry Kevin Laatz
2018-10-04 13:00   ` [PATCH v2 00/10] introduce telemetry library Van Haaren, Harry
2018-10-04 13:25   ` Van Haaren, Harry
2018-10-04 15:16     ` Gaëtan Rivet
2018-10-04 15:53     ` Thomas Monjalon
2018-10-05 22:05       ` Gaëtan Rivet
2018-10-09 10:33       ` Van Haaren, Harry
2018-10-09 11:41         ` Thomas Monjalon
2018-10-09 14:56           ` Bruce Richardson
2018-10-09 17:07             ` Thomas Monjalon
2018-10-10 10:51   ` [PATCH v3 00/12] " Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 01/12] eal: add param register infrastructure Kevin Laatz
2018-10-10 12:28       ` Thomas Monjalon
2018-10-10 10:51     ` [PATCH v3 02/12] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 03/12] telemetry: add initial connection socket Kevin Laatz
2018-10-10 12:24       ` Thomas Monjalon
2018-10-10 10:51     ` [PATCH v3 04/12] telemetry: add client feature and sockets Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 05/12] telemetry: add parser for client socket messages Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 06/12] telemetry: update metrics before sending stats Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 07/12] telemetry: format json response when " Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 08/12] telemetry: add tests for telemetry api Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 09/12] telemetry: add ability to disable selftest Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 10/12] doc: add telemetry documentation Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 11/12] usertools: add client python script for telemetry Kevin Laatz
2018-10-10 10:51     ` [PATCH v3 12/12] build: add dependency on telemetry to apps in meson Kevin Laatz
2018-10-11 16:58     ` [PATCH v4 00/13] introduce telemetry library Kevin Laatz
2018-10-11 16:58       ` [PATCH v4 01/13] eal: add param register infrastructure Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-16 13:42         ` Thomas Monjalon
2018-10-16 14:20           ` Laatz, Kevin
2018-10-16 15:13             ` Thomas Monjalon
2018-10-11 16:58       ` [PATCH v4 02/13] eal: make get runtime dir function public Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 04/13] telemetry: add initial connection socket Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 05/13] telemetry: add client feature and sockets Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 06/13] telemetry: add parser for client socket messages Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 07/13] telemetry: update metrics before sending stats Kevin Laatz
2018-10-16  0:45         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 08/13] telemetry: format json response when " Kevin Laatz
2018-10-16  0:46         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 09/13] telemetry: add tests for telemetry api Kevin Laatz
2018-10-16  0:46         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 10/13] telemetry: add ability to disable selftest Kevin Laatz
2018-10-16  0:47         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 11/13] doc: add telemetry documentation Kevin Laatz
2018-10-16  0:47         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 12/13] usertools: add client python script for telemetry Kevin Laatz
2018-10-16  0:47         ` Van Haaren, Harry
2018-10-11 16:58       ` [PATCH v4 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
2018-10-16  0:47         ` Van Haaren, Harry
2018-10-16 15:57       ` [PATCH v5 00/13] introduce telemetry library Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 01/13] eal: add param register infrastructure Kevin Laatz
2018-10-17  9:41           ` Thomas Monjalon
2018-10-17 11:45             ` Gaëtan Rivet
2018-10-17 13:46               ` Thomas Monjalon
2018-10-17 14:09                 ` Laatz, Kevin
2018-10-17 14:20                   ` Thomas Monjalon
2018-10-17 13:55               ` Laatz, Kevin
2018-10-17 15:56           ` Gaëtan Rivet
2018-10-18 15:58             ` Laatz, Kevin
2018-10-16 15:57         ` [PATCH v5 02/13] eal: make get runtime dir function public Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 04/13] telemetry: add initial connection socket Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 05/13] telemetry: add client feature and sockets Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 06/13] telemetry: add parser for client socket messages Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 07/13] telemetry: update metrics before sending stats Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 08/13] telemetry: format json response when " Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 09/13] telemetry: add tests for telemetry api Kevin Laatz
2018-10-16 15:57         ` [PATCH v5 10/13] telemetry: add ability to disable selftest Kevin Laatz
2018-10-16 15:58         ` [PATCH v5 11/13] doc: add telemetry documentation Kevin Laatz
2018-10-16 15:58         ` [PATCH v5 12/13] usertools: add client python script for telemetry Kevin Laatz
2018-10-16 15:58         ` [PATCH v5 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
2018-10-18  8:07         ` [PATCH v5 00/13] introduce telemetry library Mattias Rönnblom
2018-10-19 10:16           ` Laatz, Kevin
2018-10-22  7:11             ` Mattias Rönnblom
2018-10-22  9:03               ` Laatz, Kevin
2018-10-22 11:00         ` [PATCH v6 " Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 01/13] eal: add option register infrastructure Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 02/13] eal: make get runtime dir function public Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 04/13] telemetry: add initial connection socket Kevin Laatz
2018-10-22 13:50             ` Mattias Rönnblom
2018-10-22 11:00           ` [PATCH v6 05/13] telemetry: add client feature and sockets Kevin Laatz
2018-10-22 14:05             ` Mattias Rönnblom
2018-10-23  8:42               ` Laatz, Kevin
2018-10-22 11:00           ` [PATCH v6 06/13] telemetry: add parser for client socket messages Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 07/13] telemetry: update metrics before sending stats Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 08/13] telemetry: format json response when " Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 09/13] telemetry: add tests for telemetry api Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 10/13] telemetry: add ability to disable selftest Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 11/13] doc: add telemetry documentation Kevin Laatz
2018-10-22 12:25             ` Kovacevic, Marko
2018-10-22 11:00           ` [PATCH v6 12/13] usertools: add client python script for telemetry Kevin Laatz
2018-10-22 11:00           ` [PATCH v6 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
2018-10-24 13:27           ` [PATCH v7 00/13] introduce telemetry library Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 01/13] eal: add option register infrastructure Kevin Laatz
2018-10-24 14:01               ` Gaëtan Rivet
2018-10-24 14:33                 ` Thomas Monjalon
2018-10-24 14:52                   ` Laatz, Kevin
2018-10-24 15:05                     ` Laatz, Kevin
2018-10-24 13:27             ` [PATCH v7 02/13] eal: make get runtime dir function public Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 04/13] telemetry: add initial connection socket Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 05/13] telemetry: add client feature and sockets Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 06/13] telemetry: add parser for client socket messages Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 07/13] telemetry: update metrics before sending stats Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 08/13] telemetry: format json response when " Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 09/13] telemetry: add tests for telemetry api Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 10/13] telemetry: add ability to disable selftest Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 11/13] doc: add telemetry documentation Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 12/13] usertools: add client python script for telemetry Kevin Laatz
2018-10-24 13:27             ` [PATCH v7 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
2018-10-24 14:13             ` [PATCH v7 00/13] introduce telemetry library Thomas Monjalon
2018-10-24 14:49               ` Laatz, Kevin
2018-10-24 16:02             ` [PATCH v8 " Kevin Laatz
2018-10-24 16:02               ` [PATCH v8 01/13] eal: add option register infrastructure Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 02/13] eal: make get runtime dir function public Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 03/13] telemetry: initial telemetry infrastructure Kevin Laatz
2018-10-25 20:33                 ` Thomas Monjalon
2018-10-24 16:03               ` [PATCH v8 04/13] telemetry: add initial connection socket Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 05/13] telemetry: add client feature and sockets Kevin Laatz
2018-10-25 20:29                 ` Thomas Monjalon
2018-10-25 20:41                   ` Thomas Monjalon
2018-10-25 20:44                     ` Bruce Richardson
2018-10-25 20:49                       ` Thomas Monjalon
2018-10-25 21:16                         ` Richardson, Bruce
2018-10-25 23:58                           ` Thomas Monjalon
2018-10-24 16:03               ` [PATCH v8 06/13] telemetry: add parser for client socket messages Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 07/13] telemetry: update metrics before sending stats Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 08/13] telemetry: format json response when " Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 09/13] telemetry: add tests for telemetry api Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 10/13] telemetry: add ability to disable selftest Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 11/13] doc: add telemetry documentation Kevin Laatz
2018-10-25 20:31                 ` Thomas Monjalon
2018-10-24 16:03               ` [PATCH v8 12/13] usertools: add client python script for telemetry Kevin Laatz
2018-10-24 16:03               ` [PATCH v8 13/13] build: add dependency on telemetry to apps in meson Kevin Laatz
2018-10-26 23:59               ` [PATCH v9 00/12] Introduce Telemetry Library Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 01/12] eal: add option register infrastructure Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 02/12] eal: make get runtime dir function public Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
2018-10-27  1:56                   ` Thomas Monjalon
2018-10-27  2:19                     ` Van Haaren, Harry
2018-10-27  2:33                       ` Thomas Monjalon
2018-10-26 23:59                 ` [PATCH v9 04/12] telemetry: add initial connection socket Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 05/12] telemetry: add client feature and sockets Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 06/12] telemetry: add parser for client socket messages Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 07/12] telemetry: update metrics before sending stats Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 08/12] telemetry: format json response when " Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 09/12] telemetry: add ability to disable selftest Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 10/12] doc: add telemetry documentation Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 11/12] usertools: add client python script for telemetry Harry van Haaren
2018-10-26 23:59                 ` [PATCH v9 12/12] build: add dependency on telemetry to apps in meson Harry van Haaren
2018-10-27  9:17                 ` [PATCH v10 00/12] Introduce Telemetry Library Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 01/12] eal: add option register infrastructure Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 02/12] eal: make get runtime dir function public Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 03/12] telemetry: initial telemetry infrastructure Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 04/12] telemetry: add initial connection socket Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 05/12] telemetry: add client feature and sockets Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 06/12] telemetry: add parser for client socket messages Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 07/12] telemetry: update metrics before sending stats Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 08/12] telemetry: format json response when " Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 09/12] telemetry: add ability to disable selftest Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 10/12] doc: add telemetry documentation Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 11/12] usertools: add client python script for telemetry Harry van Haaren
2018-10-27  9:17                   ` [PATCH v10 12/12] build: add dependency on telemetry to apps in meson Harry van Haaren
2018-10-27 13:24                   ` [PATCH v10 00/12] Introduce Telemetry Library Thomas Monjalon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.