diff options
author | Ole Troan <ot@cisco.com> | 2018-08-31 00:29:48 +0200 |
---|---|---|
committer | Damjan Marion <dmarion@me.com> | 2018-09-02 16:13:57 +0000 |
commit | 732021070fa0c731896ab3e29f802d3834c72ab7 (patch) | |
tree | d7ecbd4e4c986333b1ba59db931f0e3283da2195 /src | |
parent | 855e26868ff8b9e6d00ca4d69ce6c9fdc0f2e121 (diff) |
STATS: Python binding to access VPP statistics and counters.
from vpp_papi.vpp_stats import VPPStats
s = VPPStats(socketname='/var/run/stats.sock')
c = s.ls('/if/rx')
counters = s.dump(c)
print(s.set_error_str())
Change-Id: I203ebe60b0c9ee5742aadc737c0f29051757959d
Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/vpp-api/client/libvppapiclient.map | 2 | ||||
-rw-r--r-- | src/vpp-api/client/stat_client.c | 23 | ||||
-rw-r--r-- | src/vpp-api/client/stat_client.h | 9 | ||||
-rw-r--r-- | src/vpp-api/python/vpp_papi/vpp_stats.py | 161 | ||||
-rw-r--r-- | src/vpp/app/vpp_get_stats.c | 2 |
5 files changed, 188 insertions, 9 deletions
diff --git a/src/vpp-api/client/libvppapiclient.map b/src/vpp-api/client/libvppapiclient.map index 81391ea7687..f51b92403da 100644 --- a/src/vpp-api/client/libvppapiclient.map +++ b/src/vpp-api/client/libvppapiclient.map @@ -21,5 +21,7 @@ VPPAPICLIENT_18.10 { stat_segment_dump; stat_segment_data_free; stat_segment_heartbeat; + stat_segment_string_vector; + stat_segment_vec_len; local: *; }; diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index d7d20452583..ad7078ea745 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -112,7 +112,7 @@ stat_segment_register (u8 * stats[]) p = hash_get_mem (sm->counter_vector_by_name, stats[i]); if (p == 0) { - fprintf (stderr, "WARN: %s not in directory!", stats[i]); + fprintf (stderr, "WARN: %s not in directory!\n", stats[i]); continue; } vec_add2 (cached_pointer_vec, cp, 1); @@ -183,7 +183,7 @@ maybe_update_cached_pointers (stat_segment_cached_pointer_t * cached_pointers) p = hash_get_mem (sm->counter_vector_by_name, cp->name); if (p == 0) { - fprintf (stderr, "WARN: %s not in directory!", cp->name); + fprintf (stderr, "WARN: %s not in directory!\n", cp->name); continue; } cp->ep = (stat_segment_directory_entry_t *) (p[0]); @@ -351,7 +351,7 @@ stat_segment_dump (u8 * stats[]) p = hash_get_mem (sm->counter_vector_by_name, stats[i]); if (p == 0) { - fprintf (stderr, "WARN: %s not in directory!", stats[i]); + fprintf (stderr, "WARN: %s not in directory!\n", stats[i]); continue; } /* Collect counter */ @@ -363,6 +363,23 @@ stat_segment_dump (u8 * stats[]) return res; } +/* Wrapper for accessing vectors from other languages */ +int +stat_segment_vec_len (void *vec) +{ + return vec_len (vec); +} + +/* Create a vector from a string (or add to existing) */ +u8 ** +stat_segment_string_vector (u8 ** string_vector, char *string) +{ + u8 *name = 0; + name = vec_dup ((u8 *) string); + vec_add1 (string_vector, (u8 *) name); + return string_vector; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vpp-api/client/stat_client.h b/src/vpp-api/client/stat_client.h index 5f076686fdd..73e91b1881b 100644 --- a/src/vpp-api/client/stat_client.h +++ b/src/vpp-api/client/stat_client.h @@ -42,16 +42,15 @@ typedef struct int stat_segment_connect (char *socket_name); void stat_segment_disconnect (void); - u8 **stat_segment_ls (u8 ** pattern); stat_segment_data_t *stat_segment_dump (u8 ** counter_vec); - stat_segment_cached_pointer_t *stat_segment_register (u8 ** counter_vec); -stat_segment_data_t *stat_segment_collect (stat_segment_cached_pointer_t *); /* Collects registered counters */ - +/* Collects registered counters */ +stat_segment_data_t *stat_segment_collect (stat_segment_cached_pointer_t *); void stat_segment_data_free (stat_segment_data_t * res); - f64 stat_segment_heartbeat (void); +u8 **stat_segment_string_vector (u8 ** string_vector, char *string); +int stat_segment_vec_len (void *vec); #endif /* included_stat_client_h */ diff --git a/src/vpp-api/python/vpp_papi/vpp_stats.py b/src/vpp-api/python/vpp_papi/vpp_stats.py new file mode 100644 index 00000000000..b84a0e5dc9d --- /dev/null +++ b/src/vpp-api/python/vpp_papi/vpp_stats.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +from __future__ import print_function +from cffi import FFI + +ffi = FFI() +ffi.cdef(""" +typedef uint64_t counter_t; +typedef struct { + counter_t packets; + counter_t bytes; +} vlib_counter_t; + +typedef enum { + STAT_DIR_TYPE_ILLEGAL = 0, + STAT_DIR_TYPE_SCALAR_POINTER, + STAT_DIR_TYPE_VECTOR_POINTER, + STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE, + STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED, + STAT_DIR_TYPE_ERROR_INDEX, + STAT_DIR_TYPE_SERIALIZED_NODES, +} stat_directory_type_t; + +typedef struct { + stat_directory_type_t type; + void *value; +} stat_segment_directory_entry_t; + +typedef struct { + char *name; + stat_directory_type_t type; + union { + double scalar_value; + uint64_t error_value; + uint64_t *vector_pointer; + counter_t **simple_counter_vec; + vlib_counter_t **combined_counter_vec; + }; +} stat_segment_data_t; + +typedef struct { + char *name; + stat_segment_directory_entry_t *ep; +} stat_segment_cached_pointer_t; + +int stat_segment_connect (char *socket_name); +void stat_segment_disconnect (void); + +uint8_t **stat_segment_ls (uint8_t **pattern); +stat_segment_data_t *stat_segment_dump (uint8_t ** counter_vec); +/* Collects registered counters */ +stat_segment_cached_pointer_t *stat_segment_register (uint8_t ** counter_vec); +stat_segment_data_t *stat_segment_collect (stat_segment_cached_pointer_t *); +void stat_segment_data_free (stat_segment_data_t * res); +double stat_segment_heartbeat (void); +int stat_segment_vec_len(void *vec); +uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string); +""") + + +# Utility functions +def make_string_vector(api, strings): + vec = ffi.NULL + if type(strings) is not list: + strings = [strings] + for s in strings: + vec = api.stat_segment_string_vector(vec, ffi.new("char []", s)) + return vec + + +def make_string_list(api, vec): + vec_len = api.stat_segment_vec_len(vec) + return [ffi.string(vec[i]) for i in range(vec_len)] + + +# 2-dimensonal array of thread, index +def simple_counter_vec_list(api, e): + vec = [] + for thread in range(api.stat_segment_vec_len(e)): + len_interfaces = api.stat_segment_vec_len(e[thread]) + if_per_thread = [e[thread][interfaces] + for interfaces in range(len_interfaces)] + vec.append(if_per_thread) + return vec + + +def vlib_counter_dict(c): + return {'packets': c.packets, + 'bytes': c.bytes} + + +def combined_counter_vec_list(api, e): + vec = [] + for thread in range(api.stat_segment_vec_len(e)): + len_interfaces = api.stat_segment_vec_len(e[thread]) + if_per_thread = [vlib_counter_dict(e[thread][interfaces]) + for interfaces in range(len_interfaces)] + vec.append(if_per_thread) + return vec + + +def stat_entry_to_python(api, e): + if e.type == 3: + return simple_counter_vec_list(e.simple_counter_vec) + if e.type == 4: + return combined_counter_vec_list(api, e.combined_counter_vec) + if e.type == 5: + return e.error_value + return None + + +class VPPStats: + def __init__(self, socketname='/var/run/stats.sock'): + self.api = ffi.dlopen('libvppapiclient.so') + rv = self.api.stat_segment_connect(socketname) + if rv != 0: + raise IOError() + + def heartbeat(self): + return self.api.stat_segment_heartbeat() + + def ls(self, patterns): + return self.api.stat_segment_ls(make_string_vector(self.api, patterns)) + + def dump(self, counters): + stats = {} + rv = self.api.stat_segment_dump(counters) + rv_len = self.api.stat_segment_vec_len(rv) + for i in range(rv_len): + n = ffi.string(rv[i].name) + e = stat_entry_to_python(self.api, rv[i]) + if e: + stats[n] = e + return stats + + def dump_str(self, counters_str): + return self.dump(make_string_vector(self.api, counters_str)) + + def disconnect(self): + self.api.stat_segment_disconnect() + + def set_errors(self): + '''Return all errors counters > 0''' + error_names = self.ls(['/err/']) + error_counters = self.dump(error_names) + return {k: error_counters[k] + for k in error_counters.keys() if error_counters[k]} + + def set_errors_str(self): + '''Return all errors counters > 0 pretty printed''' + s = 'ERRORS:\n' + error_counters = self.set_errors() + for k in sorted(error_counters): + s += '{:<60}{:>10}\n'.format(k, error_counters[k]) + return s + + def register(self): + raise NotImplemented + + def collect(self): + raise NotImplemented diff --git a/src/vpp/app/vpp_get_stats.c b/src/vpp/app/vpp_get_stats.c index c1f43a747ed..908e675b062 100644 --- a/src/vpp/app/vpp_get_stats.c +++ b/src/vpp/app/vpp_get_stats.c @@ -186,7 +186,7 @@ reconnect: break; case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: - for (k = 0; k < vec_len (res[i].simple_counter_vec); k++) + for (k = 0; k < vec_len (res[i].combined_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, |