STATS: Python binding to access VPP statistics and counters.
[vpp.git] / src / vpp-api / python / vpp_papi / vpp_stats.py
1 #!/usr/bin/env python
2
3 from __future__ import print_function
4 from cffi import FFI
5
6 ffi = FFI()
7 ffi.cdef("""
8 typedef uint64_t counter_t;
9 typedef struct {
10   counter_t packets;
11   counter_t bytes;
12 } vlib_counter_t;
13
14 typedef enum {
15   STAT_DIR_TYPE_ILLEGAL = 0,
16   STAT_DIR_TYPE_SCALAR_POINTER,
17   STAT_DIR_TYPE_VECTOR_POINTER,
18   STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
19   STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
20   STAT_DIR_TYPE_ERROR_INDEX,
21   STAT_DIR_TYPE_SERIALIZED_NODES,
22 } stat_directory_type_t;
23
24 typedef struct {
25   stat_directory_type_t type;
26   void *value;
27 } stat_segment_directory_entry_t;
28
29 typedef struct {
30   char *name;
31   stat_directory_type_t type;
32   union {
33     double scalar_value;
34     uint64_t error_value;
35     uint64_t *vector_pointer;
36     counter_t **simple_counter_vec;
37     vlib_counter_t **combined_counter_vec;
38   };
39 } stat_segment_data_t;
40
41 typedef struct {
42   char *name;
43   stat_segment_directory_entry_t *ep;
44 } stat_segment_cached_pointer_t;
45
46 int stat_segment_connect (char *socket_name);
47 void stat_segment_disconnect (void);
48
49 uint8_t  **stat_segment_ls (uint8_t **pattern);
50 stat_segment_data_t *stat_segment_dump (uint8_t ** counter_vec);
51 /* Collects registered counters */
52 stat_segment_cached_pointer_t *stat_segment_register (uint8_t ** counter_vec);
53 stat_segment_data_t *stat_segment_collect (stat_segment_cached_pointer_t *);
54 void stat_segment_data_free (stat_segment_data_t * res);
55 double stat_segment_heartbeat (void);
56 int stat_segment_vec_len(void *vec);
57 uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
58 """)
59
60
61 # Utility functions
62 def make_string_vector(api, strings):
63     vec = ffi.NULL
64     if type(strings) is not list:
65         strings = [strings]
66     for s in strings:
67         vec = api.stat_segment_string_vector(vec, ffi.new("char []", s))
68     return vec
69
70
71 def make_string_list(api, vec):
72     vec_len = api.stat_segment_vec_len(vec)
73     return [ffi.string(vec[i]) for i in range(vec_len)]
74
75
76 # 2-dimensonal array of thread, index
77 def simple_counter_vec_list(api, e):
78     vec = []
79     for thread in range(api.stat_segment_vec_len(e)):
80         len_interfaces = api.stat_segment_vec_len(e[thread])
81         if_per_thread = [e[thread][interfaces]
82                          for interfaces in range(len_interfaces)]
83         vec.append(if_per_thread)
84     return vec
85
86
87 def vlib_counter_dict(c):
88     return {'packets': c.packets,
89             'bytes': c.bytes}
90
91
92 def combined_counter_vec_list(api, e):
93     vec = []
94     for thread in range(api.stat_segment_vec_len(e)):
95         len_interfaces = api.stat_segment_vec_len(e[thread])
96         if_per_thread = [vlib_counter_dict(e[thread][interfaces])
97                          for interfaces in range(len_interfaces)]
98         vec.append(if_per_thread)
99     return vec
100
101
102 def stat_entry_to_python(api, e):
103     if e.type == 3:
104         return simple_counter_vec_list(e.simple_counter_vec)
105     if e.type == 4:
106         return combined_counter_vec_list(api, e.combined_counter_vec)
107     if e.type == 5:
108         return e.error_value
109     return None
110
111
112 class VPPStats:
113     def __init__(self, socketname='/var/run/stats.sock'):
114         self.api = ffi.dlopen('libvppapiclient.so')
115         rv = self.api.stat_segment_connect(socketname)
116         if rv != 0:
117             raise IOError()
118
119     def heartbeat(self):
120         return self.api.stat_segment_heartbeat()
121
122     def ls(self, patterns):
123         return self.api.stat_segment_ls(make_string_vector(self.api, patterns))
124
125     def dump(self, counters):
126         stats = {}
127         rv = self.api.stat_segment_dump(counters)
128         rv_len = self.api.stat_segment_vec_len(rv)
129         for i in range(rv_len):
130             n = ffi.string(rv[i].name)
131             e = stat_entry_to_python(self.api, rv[i])
132             if e:
133                 stats[n] = e
134         return stats
135
136     def dump_str(self, counters_str):
137         return self.dump(make_string_vector(self.api, counters_str))
138
139     def disconnect(self):
140         self.api.stat_segment_disconnect()
141
142     def set_errors(self):
143         '''Return all errors counters > 0'''
144         error_names = self.ls(['/err/'])
145         error_counters = self.dump(error_names)
146         return {k: error_counters[k]
147                 for k in error_counters.keys() if error_counters[k]}
148
149     def set_errors_str(self):
150         '''Return all errors counters > 0 pretty printed'''
151         s = 'ERRORS:\n'
152         error_counters = self.set_errors()
153         for k in sorted(error_counters):
154             s += '{:<60}{:>10}\n'.format(k, error_counters[k])
155         return s
156
157     def register(self):
158         raise NotImplemented
159
160     def collect(self):
161         raise NotImplemented