aboutsummaryrefslogtreecommitdiffstats
path: root/src/vpp/app
diff options
context:
space:
mode:
authorOle Troan <ot@cisco.com>2018-08-23 13:00:53 +0200
committerDamjan Marion <dmarion@me.com>2018-08-29 09:39:38 +0000
commit2fee16787ed0d622631223567635a77e14c8c076 (patch)
treeaab9492cc925b7cfa769d996fd6fbbf1d0506971 /src/vpp/app
parentfcf9497d3bcd34b8b5090ee053575296cf56c5e6 (diff)
STATS: stat_client updates.
New stat segment client library: vpp-api/client/stat_client.h New stat segment query app: vpp_get_stats [ls | dump | poll ] <pattern ...> Prometheus integration through: vpp_prometheus_export <pattern> Change-Id: I6f370cf599e9fcf066f22965a62d3a8acd529994 Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/vpp/app')
-rw-r--r--src/vpp/app/stat_client.c378
-rw-r--r--src/vpp/app/stat_client.h66
-rw-r--r--src/vpp/app/vpp_get_stats.c242
-rw-r--r--src/vpp/app/vpp_prometheus_export.c320
4 files changed, 562 insertions, 444 deletions
diff --git a/src/vpp/app/stat_client.c b/src/vpp/app/stat_client.c
deleted file mode 100644
index 2bedf6f2d7d..00000000000
--- a/src/vpp/app/stat_client.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- *------------------------------------------------------------------
- * stat_client.c
- *
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <vpp/app/stat_client.h>
-
-stat_client_main_t stat_client_main;
-
-static int
-stat_segment_connect (stat_client_main_t * sm)
-{
- ssvm_private_t *ssvmp = &sm->stat_segment;
- ssvm_shared_header_t *shared_header;
- clib_socket_t s = { 0 };
- clib_error_t *err;
- int fd = -1, retval;
-
- s.config = (char *) sm->socket_name;
- s.flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET;
- err = clib_socket_init (&s);
- if (err)
- {
- clib_error_report (err);
- exit (1);
- }
- err = clib_socket_recvmsg (&s, 0, 0, &fd, 1);
- if (err)
- {
- clib_error_report (err);
- return -1;
- }
- clib_socket_close (&s);
-
- memset (ssvmp, 0, sizeof (*ssvmp));
- ssvmp->fd = fd;
-
- /* Note: this closes memfd.fd */
- retval = ssvm_slave_init_memfd (ssvmp);
- if (retval)
- {
- clib_warning ("WARNING: segment map returned %d", retval);
- return -1;
- }
-
- fformat (stdout, "Stat segment mapped OK...\n");
-
- ASSERT (ssvmp && ssvmp->sh);
-
- /* Pick up the segment lock from the shared memory header */
- shared_header = ssvmp->sh;
- sm->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
- sm->segment_ready = 1;
-
- return 0;
-}
-
-#define foreach_cached_pointer \
-_(/sys/vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr) \
-_(/sys/input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr) \
-_(/sys/last_update, SCALAR_POINTER, &stat_client_main.last_runtime_ptr) \
-_(/sys/last_stats_clear, SCALAR_POINTER, \
- &stat_client_main.last_runtime_stats_clear_ptr) \
-_(/if/rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters) \
-_(/if/tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters) \
-_(/err/0/counter_vector, VECTOR_POINTER, \
- &stat_client_main.thread_0_error_counts) \
-_(serialized_nodes, SERIALIZED_NODES, \
- &stat_client_main.serialized_nodes)
-
-typedef struct
-{
- char *name;
- stat_directory_type_t type;
- void *valuep;
-} cached_pointer_t;
-
-cached_pointer_t cached_pointers[] = {
-#define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
- foreach_cached_pointer
-#undef _
-};
-
-static void
-maybe_update_cached_pointers (stat_client_main_t * sm,
- ssvm_shared_header_t * shared_header)
-{
- uword *p, *counter_vector_by_name;
- int i;
- stat_segment_directory_entry_t *ep;
- cached_pointer_t *cp;
- u64 *valuep;
-
- /* Cached pointers OK? */
- if (sm->current_epoch ==
- (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
- return;
-
- fformat (stdout, "Updating cached pointers...\n");
-
- /* Nope, fix them... */
- counter_vector_by_name = (uword *)
- shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
-
- for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
- {
- cp = &cached_pointers[i];
-
- p = hash_get_mem (counter_vector_by_name, cp->name);
-
- if (p == 0)
- {
- clib_warning ("WARN: %s not in directory!", cp->name);
- continue;
- }
- ep = (stat_segment_directory_entry_t *) (p[0]);
- ASSERT (ep->type == cp->type);
- valuep = (u64 *) cp->valuep;
- *valuep = (u64) ep->value;
- }
-
- /* And remember that we did... */
- sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
-}
-
-static void
-stat_poll_loop (stat_client_main_t * sm)
-{
- struct timespec ts, tsrem;
- ssvm_private_t *ssvmp = &sm->stat_segment;
- ssvm_shared_header_t *shared_header;
- vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
- vlib_node_t ***nodes_by_thread;
- vlib_node_t **nodes;
- vlib_node_t *n;
- f64 vector_rate, input_rate;
- u32 len;
- int i, j;
- u32 source_address_match_errors;
-
- /* Wait until the stats segment is mapped */
- while (!sm->segment_ready)
- {
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000;
- while (nanosleep (&ts, &tsrem) < 0)
- ts = tsrem;
- }
-
- shared_header = ssvmp->sh;
- ASSERT (ssvmp->sh);
-
- while (1)
- {
- /* Scrape stats every 5 seconds */
- ts.tv_sec = 5;
- ts.tv_nsec = 0;
- while (nanosleep (&ts, &tsrem) < 0)
- ts = tsrem;
-
- vec_reset_length (thread0_rx_counters);
- vec_reset_length (thread0_tx_counters);
-
- /* Grab the stats segment lock */
- clib_spinlock_lock (sm->stat_segment_lockp);
-
- /* see if we need to update cached pointers */
- maybe_update_cached_pointers (sm, shared_header);
-
- ASSERT (sm->vector_rate_ptr);
- ASSERT (sm->intfc_rx_counters);
- ASSERT (sm->intfc_tx_counters);
-
- /* Read data from the segment */
- vector_rate = *sm->vector_rate_ptr;
- input_rate = *sm->input_rate_ptr;
-
- len = vec_len (sm->intfc_rx_counters[0]);
-
- ASSERT (len);
-
- vec_validate (thread0_rx_counters, len - 1);
- vec_validate (thread0_tx_counters, len - 1);
-
- clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
- len * sizeof (vlib_counter_t));
- clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
- len * sizeof (vlib_counter_t));
-
- source_address_match_errors =
- sm->thread_0_error_counts[sm->source_address_match_error_index];
-
- /* Drop the lock */
- clib_spinlock_unlock (sm->stat_segment_lockp);
-
- /* And print results... */
-
- fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
- vector_rate, input_rate);
-
- for (i = 0; i < vec_len (thread0_rx_counters); i++)
- {
- fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
- i, thread0_rx_counters[i].packets,
- thread0_rx_counters[i].bytes);
- fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
- i, thread0_tx_counters[i].packets,
- thread0_tx_counters[i].bytes);
- }
-
- fformat (stdout, "%lld source address match errors\n",
- source_address_match_errors);
-
- if (sm->serialized_nodes)
- {
- nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
-
- /* Across all threads... */
- for (i = 0; i < vec_len (nodes_by_thread); i++)
- {
- u64 n_input, n_output, n_drop, n_punt;
- u64 n_internal_vectors, n_internal_calls;
- u64 n_clocks, l, v, c;
- f64 dt;
-
- nodes = nodes_by_thread[i];
-
- fformat (stdout, "Thread %d -------------------------\n", i);
-
- n_input = n_output = n_drop = n_punt = n_clocks = 0;
- n_internal_vectors = n_internal_calls = 0;
-
- /* Across all nodes */
- for (j = 0; j < vec_len (nodes); j++)
- {
- n = nodes[j];
-
- /* Exactly stolen from node_cli.c... */
- l = n->stats_total.clocks - n->stats_last_clear.clocks;
- n_clocks += l;
-
- v = n->stats_total.vectors - n->stats_last_clear.vectors;
- c = n->stats_total.calls - n->stats_last_clear.calls;
-
- switch (n->type)
- {
- default:
- vec_free (n->name);
- vec_free (n->next_nodes);
- vec_free (n);
- continue;
-
- case VLIB_NODE_TYPE_INTERNAL:
- n_output +=
- (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
- n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
- n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
- if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
- {
- n_internal_vectors += v;
- n_internal_calls += c;
- }
- if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
- n_input += v;
- break;
-
- case VLIB_NODE_TYPE_INPUT:
- n_input += v;
- break;
- }
-
- if (n->stats_total.calls)
- {
- fformat (stdout,
- "%s (%s): clocks %lld calls %lld vectors %lld ",
- n->name,
- n->state_string,
- n->stats_total.clocks,
- n->stats_total.calls, n->stats_total.vectors);
- if (n->stats_total.vectors)
- fformat (stdout, "clocks/pkt %.2f\n",
- (f64) n->stats_total.clocks /
- (f64) n->stats_total.vectors);
- else
- fformat (stdout, "\n");
- }
- vec_free (n->name);
- vec_free (n->next_nodes);
- vec_free (n);
- }
-
- fformat (stdout, "average vectors/node %.2f\n",
- (n_internal_calls > 0
- ? (f64) n_internal_vectors / (f64) n_internal_calls
- : 0));
-
-
- dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
-
- fformat (stdout,
- " vectors rates in %.4e, out %.4e, drop %.4e, "
- "punt %.4e\n",
- (f64) n_input / dt,
- (f64) n_output / dt, (f64) n_drop / dt,
- (f64) n_punt / dt);
-
- vec_free (nodes);
- }
- vec_free (nodes_by_thread);
- }
- else
- {
- fformat (stdout, "serialized nodes NULL?\n");
- }
-
- }
-}
-
-int
-main (int argc, char **argv)
-{
- unformat_input_t _argv, *a = &_argv;
- stat_client_main_t *sm = &stat_client_main;
- u8 *stat_segment_name;
- int rv;
-
- clib_mem_init (0, 128 << 20);
-
- unformat_init_command_line (a, argv);
-
- stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
-
- while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (a, "socket-name %s", &stat_segment_name))
- ;
- else
- {
- fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
- exit (1);
- }
- }
-
- sm->socket_name = stat_segment_name;
-
- rv = stat_segment_connect (sm);
- if (rv)
- {
- fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
- stat_segment_name);
- exit (1);
- }
-
- stat_poll_loop (sm);
- exit (0);
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vpp/app/stat_client.h b/src/vpp/app/stat_client.h
deleted file mode 100644
index 97bd3f9bb0b..00000000000
--- a/src/vpp/app/stat_client.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2018 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef __included_stat_client_h__
-#define __included_stat_client_h__
-
-#include <vlib/vlib.h>
-#include <vppinfra/socket.h>
-#include <svm/ssvm.h>
-#include <vpp/stats/stats.h>
-
-typedef struct
-{
- u64 current_epoch;
-
- /* Cached pointers to scalar quantities, these wont change */
- f64 *vector_rate_ptr;
- f64 *input_rate_ptr;
- f64 *last_runtime_ptr;
- f64 *last_runtime_stats_clear_ptr;
-
- volatile int segment_ready;
-
- /*
- * Cached pointers to vector quantities,
- * MUST invalidate when the epoch changes
- */
- vlib_counter_t **intfc_rx_counters;
- vlib_counter_t **intfc_tx_counters;
- u8 *serialized_nodes;
-
- u64 *thread_0_error_counts;
- u64 source_address_match_error_index;
-
- /* mapped stats segment object */
- ssvm_private_t stat_segment;
-
- /* Spinlock for the stats segment */
- clib_spinlock_t *stat_segment_lockp;
-
- u8 *socket_name;
-} stat_client_main_t;
-
-extern stat_client_main_t stat_client_main;
-
-#endif /* __included_stat_client_h__ */
-
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vpp/app/vpp_get_stats.c b/src/vpp/app/vpp_get_stats.c
new file mode 100644
index 00000000000..86e511e56f2
--- /dev/null
+++ b/src/vpp/app/vpp_get_stats.c
@@ -0,0 +1,242 @@
+/*
+ *------------------------------------------------------------------
+ * vpp_get_stats.c
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vpp-api/client/stat_client.h>
+#include <vlib/vlib.h>
+#include <vpp/stats/stats.h>
+
+static int
+stat_poll_loop (stat_segment_cached_pointer_t * cp)
+{
+ struct timespec ts, tsrem;
+ stat_segment_data_t *res;
+ int i, j, k, lost_connection = 0;
+ f64 heartbeat, prev_heartbeat = 0;
+
+ printf ("\033[2J"); /* clear the screen */
+ while (1)
+ {
+ heartbeat = stat_segment_heartbeat ();
+ if (heartbeat > prev_heartbeat)
+ {
+ prev_heartbeat = heartbeat;
+ lost_connection = 0;
+ }
+ else
+ {
+ lost_connection++;
+ }
+ if (lost_connection > 10)
+ {
+ fformat (stderr, "Lost connection to VPP...\n");
+ return -1;
+ }
+
+ printf ("\033[H"); /* Cursor top left corner */
+ res = stat_segment_collect (cp);
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
+ fformat (stdout, "[%d]: %lld packets %s\n",
+ j, res[i].simple_counter_vec[k][j], res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
+ fformat (stdout, "[%d]: %lld packets, %lld bytes %s\n",
+ j, res[i].combined_counter_vec[k][j].packets,
+ res[i].combined_counter_vec[k][j].bytes,
+ res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ fformat (stdout, "%lld %s\n", res[i].error_value, res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ fformat (stdout, "%.2f %s\n", res[i].scalar_value, res[i].name);
+ break;
+
+ default:
+ printf ("Unknown value\n");
+ ;
+ }
+ }
+ stat_segment_data_free (res);
+ /* Scrape stats every 5 seconds */
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ while (nanosleep (&ts, &tsrem) < 0)
+ ts = tsrem;
+
+ }
+}
+
+enum stat_client_cmd_e
+{
+ STAT_CLIENT_CMD_UNKNOWN,
+ STAT_CLIENT_CMD_LS,
+ STAT_CLIENT_CMD_POLL,
+ STAT_CLIENT_CMD_DUMP,
+};
+
+int
+main (int argc, char **argv)
+{
+ unformat_input_t _argv, *a = &_argv;
+ u8 *stat_segment_name, *pattern = 0, **patterns = 0;
+ int rv;
+ enum stat_client_cmd_e cmd = STAT_CLIENT_CMD_UNKNOWN;
+
+ clib_mem_init (0, 128 << 20);
+
+ unformat_init_command_line (a, argv);
+
+ stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
+
+ while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (a, "socket-name %s", &stat_segment_name))
+ ;
+ else if (unformat (a, "ls"))
+ {
+ cmd = STAT_CLIENT_CMD_LS;
+ }
+ else if (unformat (a, "dump"))
+ {
+ cmd = STAT_CLIENT_CMD_DUMP;
+ }
+ else if (unformat (a, "poll"))
+ {
+ cmd = STAT_CLIENT_CMD_POLL;
+ }
+ else if (unformat (a, "%s", &pattern))
+ {
+ vec_add1 (patterns, pattern);
+ }
+ else
+ {
+ fformat (stderr,
+ "%s: usage [socket-name <name>] [ls|dump|poll] <patterns> ...\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+reconnect:
+ rv = stat_segment_connect ((char *) stat_segment_name);
+ if (rv)
+ {
+ fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
+ stat_segment_name);
+ exit (1);
+ }
+
+ u8 **dir;
+ int i, j, k;
+ stat_segment_data_t *res;
+ stat_segment_cached_pointer_t *cp;
+
+ dir = stat_segment_ls (patterns);
+
+ switch (cmd)
+ {
+ case STAT_CLIENT_CMD_LS:
+ /* List all counters */
+ for (i = 0; i < vec_len (dir); i++)
+ {
+ printf ("%s\n", (char *) dir[i]);
+ }
+ break;
+
+ case STAT_CLIENT_CMD_DUMP:
+ res = stat_segment_dump (dir);
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec) - 1; k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
+ fformat (stdout, "[%d @ %d]: %lld packets %s\n",
+ j, k, res[i].simple_counter_vec[k][j],
+ res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
+ fformat (stdout, "[%d @ %d]: %lld packets, %lld bytes %s\n",
+ j, k, res[i].combined_counter_vec[k][j].packets,
+ res[i].combined_counter_vec[k][j].bytes,
+ res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ fformat (stdout, "%lld %s\n", res[i].error_value, dir[i]);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ fformat (stdout, "%.2f %s\n", dir[i], res[i].scalar_value,
+ res[i].name);
+ break;
+
+ default:
+ ;
+ }
+ }
+ stat_segment_data_free (res);
+ break;
+
+ case STAT_CLIENT_CMD_POLL:
+ cp = stat_segment_register (dir);
+ if (!cp)
+ {
+ fformat (stderr,
+ "Couldn't register required counters with stat segment\n");
+ exit (1);
+ }
+ stat_poll_loop (cp);
+ /* We can only exist the pool loop if we lost connection to VPP */
+ stat_segment_disconnect ();
+ goto reconnect;
+ break;
+
+ default:
+ fformat (stderr,
+ "%s: usage [socket-name <name>] [ls|dump|poll] <patterns> ...\n",
+ argv[0]);
+ }
+
+ stat_segment_disconnect ();
+
+ exit (0);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/app/vpp_prometheus_export.c b/src/vpp/app/vpp_prometheus_export.c
new file mode 100644
index 00000000000..57c65178c73
--- /dev/null
+++ b/src/vpp/app/vpp_prometheus_export.c
@@ -0,0 +1,320 @@
+/*
+ *------------------------------------------------------------------
+ * vpp_get_stats.c
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <arpa/inet.h>
+#include <sys/epoll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <vpp-api/client/stat_client.h>
+#include <vlib/vlib.h>
+#include <vpp/stats/stats.h>
+#include <ctype.h>
+
+/* https://github.com/prometheus/prometheus/wiki/Default-port-allocations */
+#define SERVER_PORT 9477
+
+static char *
+prom_string (char *s)
+{
+ char *p = s;
+ while (*p)
+ {
+ if (!isalnum (*p))
+ *p = '_';
+ p++;
+ }
+ return s;
+}
+
+static void
+dump_metrics (FILE * stream, stat_segment_cached_pointer_t * cp)
+{
+ stat_segment_data_t *res;
+ int i, j, k;
+
+ res = stat_segment_collect (cp);
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
+ fformat (stream, "%s{thread=\"%d\",interface=\"%d\"} %lld\n",
+ prom_string (res[i].name), k, j,
+ res[i].simple_counter_vec[k][j]);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ fformat (stream, "# TYPE %s_packets counter\n",
+ prom_string (res[i].name));
+ fformat (stream, "# TYPE %s_bytes counter\n",
+ prom_string (res[i].name));
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
+ {
+ fformat (stream,
+ "%s_packets{thread=\"%d\",interface=\"%d\"} %lld\n",
+ prom_string (res[i].name), k, j,
+ res[i].combined_counter_vec[k][j].packets);
+ fformat (stream,
+ "%s_bytes{thread=\"%d\",interface=\"%d\"} %lld\n",
+ prom_string (res[i].name), k, j,
+ res[i].combined_counter_vec[k][j].bytes);
+ }
+ break;
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
+ fformat (stream, "%s{thread=\"0\"} %lld\n",
+ prom_string (res[i].name), res[i].error_value);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
+ fformat (stream, "%s %.2f\n", prom_string (res[i].name),
+ res[i].scalar_value);
+ break;
+
+ default:
+ fformat (stderr, "Unknown value %d\n", res[i].type);
+ ;
+ }
+ }
+ stat_segment_data_free (res);
+
+}
+
+
+#define ROOTPAGE "<html><head><title>Metrics exporter</title></head><body><ul><li><a href=\"/metrics\">metrics</a></li></ul></body></html>"
+#define NOT_FOUND_ERROR "<html><head><title>Document not found</title></head><body><h1>404 - Document not found</h1></body></html>"
+
+static void
+http_handler (FILE * stream, stat_segment_cached_pointer_t * cp)
+{
+ char status[80] = { 0 };
+ if (fgets (status, sizeof (status) - 1, stream) == 0)
+ {
+ fprintf (stderr, "fgets error: %s %s\n", status, strerror (errno));
+ return;
+ }
+ char *saveptr;
+ char *method = strtok_r (status, " \t\r\n", &saveptr);
+ if (method == 0 || strncmp (method, "GET", 4) != 0)
+ {
+ fputs ("HTTP/1.0 405 Method Not Allowed\r\n", stream);
+ return;
+ }
+ char *request_uri = strtok_r (NULL, " \t", &saveptr);
+ char *protocol = strtok_r (NULL, " \t\r\n", &saveptr);
+ if (protocol == 0 || strncmp (protocol, "HTTP/1.", 7) != 0)
+ {
+ fputs ("HTTP/1.0 400 Bad Request\r\n", stream);
+ return;
+ }
+ /* Read the other headers */
+ for (;;)
+ {
+ char header[1024];
+ if (fgets (header, sizeof (header) - 1, stream) == 0)
+ {
+ fprintf (stderr, "fgets error: %s\n", strerror (errno));
+ return;
+ }
+ if (header[0] == '\n' || header[1] == '\n')
+ {
+ break;
+ }
+ }
+ if (strcmp (request_uri, "/") == 0)
+ {
+ fprintf (stream, "HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n",
+ (unsigned long) strlen (ROOTPAGE));
+ fputs (ROOTPAGE, stream);
+ return;
+ }
+ if (strcmp (request_uri, "/metrics") != 0)
+ {
+ fprintf (stream,
+ "HTTP/1.0 404 Not Found\r\nContent-Length: %lu\r\n\r\n",
+ (unsigned long) strlen (NOT_FOUND_ERROR));
+ fputs (NOT_FOUND_ERROR, stream);
+ return;
+ }
+ fputs ("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", stream);
+ dump_metrics (stream, cp);
+}
+
+static int
+start_listen (u16 port)
+{
+ struct sockaddr_in6 serveraddr;
+ int addrlen = sizeof (serveraddr);
+ int enable = 1;
+
+ int listenfd = socket (AF_INET6, SOCK_STREAM, 0);
+ if (listenfd == -1)
+ {
+ perror ("Failed opening socket");
+ }
+
+ int rv =
+ setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (int));
+ if (rv < 0)
+ {
+ perror ("Failed setsockopt");
+ }
+
+ memset (&serveraddr, 0, sizeof (serveraddr));
+ serveraddr.sin6_family = AF_INET6;
+ serveraddr.sin6_port = htons (port);
+ serveraddr.sin6_addr = in6addr_any;
+
+ if (bind (listenfd, (struct sockaddr *) &serveraddr, addrlen) < 0)
+ {
+ fprintf (stderr, "bind() error %s\n", strerror (errno));
+ return -1;
+ }
+ if (listen (listenfd, 1000000) != 0)
+ {
+ fprintf (stderr, "listen() error for %s\n", strerror (errno));
+ return -1;
+ }
+ return listenfd;
+}
+
+/* Socket epoll, linux-specific */
+union my_sockaddr
+{
+ struct sockaddr_storage storage;
+ struct sockaddr addr;
+ struct sockaddr_in sin_addr;
+ struct sockaddr_in6 sin6_addr;
+};
+
+
+
+int
+main (int argc, char **argv)
+{
+ unformat_input_t _argv, *a = &_argv;
+ u8 *stat_segment_name, *pattern = 0, **patterns = 0;
+ int rv;
+
+ clib_mem_init (0, 128 << 20);
+
+ unformat_init_command_line (a, argv);
+
+ stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
+
+ while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (a, "socket-name %s", &stat_segment_name))
+ ;
+ else if (unformat (a, "%s", &pattern))
+ {
+ vec_add1 (patterns, pattern);
+ }
+ else
+ {
+ fformat (stderr,
+ "%s: usage [socket-name <name>] <patterns> ...\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+
+ rv = stat_segment_connect ((char *) stat_segment_name);
+ if (rv)
+ {
+ fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
+ stat_segment_name);
+ exit (1);
+ }
+
+ u8 **dir;
+ stat_segment_cached_pointer_t *cp;
+
+ dir = stat_segment_ls (patterns);
+ cp = stat_segment_register (dir);
+ if (!cp)
+ {
+ fformat (stderr,
+ "Couldn't register required counters with stat segment\n");
+ exit (1);
+ }
+
+ int fd = start_listen (SERVER_PORT);
+ if (fd < 0)
+ {
+ exit (1);
+ }
+ for (;;)
+ {
+ int conn_sock = accept (fd, NULL, NULL);
+ if (conn_sock < 0)
+ {
+ fprintf (stderr, "Accept failed: %s", strerror (errno));
+ continue;
+ }
+ else
+ {
+ struct sockaddr_in6 clientaddr;
+ char address[INET6_ADDRSTRLEN];
+ socklen_t addrlen;
+ getpeername (conn_sock, (struct sockaddr *) &clientaddr, &addrlen);
+ if (inet_ntop
+ (AF_INET6, &clientaddr.sin6_addr, address, sizeof (address)))
+ {
+ printf ("Client address is [%s]:%d\n", address,
+ ntohs (clientaddr.sin6_port));
+ }
+ }
+
+ FILE *stream = fdopen (conn_sock, "r+");
+ if (stream == NULL)
+ {
+ fprintf (stderr, "fdopen error: %s\n", strerror (errno));
+ close (conn_sock);
+ continue;
+ }
+ /* Single reader at the moment */
+ http_handler (stream, cp);
+ fclose (stream);
+ }
+
+ stat_segment_disconnect ();
+
+ exit (0);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */