blob: 1a65c8ebad8f78944011fba95d65f69a25db9063 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Ole Troan73202102018-08-31 00:29:48 +02002
3from __future__ import print_function
4from cffi import FFI
Paul Vinciguerra56421092018-11-21 16:34:09 -08005import time
Ole Troan73202102018-08-31 00:29:48 +02006
7ffi = FFI()
8ffi.cdef("""
9typedef uint64_t counter_t;
10typedef struct {
11 counter_t packets;
12 counter_t bytes;
13} vlib_counter_t;
14
15typedef enum {
16 STAT_DIR_TYPE_ILLEGAL = 0,
Ole Troan58492a82018-09-04 13:19:12 +020017 STAT_DIR_TYPE_SCALAR_INDEX,
Ole Troan73202102018-08-31 00:29:48 +020018 STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
19 STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
20 STAT_DIR_TYPE_ERROR_INDEX,
Ole Troancbb8f582019-04-15 08:53:46 +020021 STAT_DIR_TYPE_NAME_VECTOR,
Ole Troan73202102018-08-31 00:29:48 +020022} stat_directory_type_t;
23
Ole Troan58492a82018-09-04 13:19:12 +020024typedef struct
25{
Ole Troan73202102018-08-31 00:29:48 +020026 stat_directory_type_t type;
27 union {
Ole Troan58492a82018-09-04 13:19:12 +020028 uint64_t index;
29 uint64_t value;
Ole Troan7d29e322020-07-21 08:46:08 +020030 uint64_t *data;
Ole Troan58492a82018-09-04 13:19:12 +020031 };
Ole Troan58492a82018-09-04 13:19:12 +020032 char name[128]; // TODO change this to pointer to "somewhere"
33} stat_segment_directory_entry_t;
34
35typedef struct
36{
37 char *name;
38 stat_directory_type_t type;
39 union
40 {
Ole Troan73202102018-08-31 00:29:48 +020041 double scalar_value;
Ole Troan233e4682019-05-16 15:01:34 +020042 counter_t *error_vector;
Ole Troan73202102018-08-31 00:29:48 +020043 counter_t **simple_counter_vec;
44 vlib_counter_t **combined_counter_vec;
Ole Troancbb8f582019-04-15 08:53:46 +020045 uint8_t **name_vector;
Ole Troan73202102018-08-31 00:29:48 +020046 };
47} stat_segment_data_t;
48
Paul Vinciguerra56421092018-11-21 16:34:09 -080049typedef struct
50{
Ole Troanb63dbc52019-06-14 10:26:14 +020051 uint64_t version;
Ole Troan7d29e322020-07-21 08:46:08 +020052 void *base;
Paul Vinciguerra56421092018-11-21 16:34:09 -080053 uint64_t epoch;
54 uint64_t in_progress;
Ole Troan7d29e322020-07-21 08:46:08 +020055 stat_segment_directory_entry_t *directory_vector;
56 uint64_t **error_vector;
Paul Vinciguerra56421092018-11-21 16:34:09 -080057} stat_segment_shared_header_t;
58
59typedef struct
60{
61 uint64_t current_epoch;
62 stat_segment_shared_header_t *shared_header;
63 stat_segment_directory_entry_t *directory_vector;
64 ssize_t memory_size;
65} stat_client_main_t;
66
67stat_client_main_t * stat_client_get(void);
68void stat_client_free(stat_client_main_t * sm);
69int stat_segment_connect_r (char *socket_name, stat_client_main_t * sm);
Ole Troan73202102018-08-31 00:29:48 +020070int stat_segment_connect (char *socket_name);
Paul Vinciguerra56421092018-11-21 16:34:09 -080071void stat_segment_disconnect_r (stat_client_main_t * sm);
Ole Troan73202102018-08-31 00:29:48 +020072void stat_segment_disconnect (void);
73
Paul Vinciguerra56421092018-11-21 16:34:09 -080074uint32_t *stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm);
Ole Troan58492a82018-09-04 13:19:12 +020075uint32_t *stat_segment_ls (uint8_t ** pattern);
Ole Troan85465582019-04-30 10:04:36 +020076stat_segment_data_t *stat_segment_dump_r (uint32_t * stats,
77 stat_client_main_t * sm);
Ole Troan58492a82018-09-04 13:19:12 +020078stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec);
Ole Troan73202102018-08-31 00:29:48 +020079void stat_segment_data_free (stat_segment_data_t * res);
Paul Vinciguerra56421092018-11-21 16:34:09 -080080
81double stat_segment_heartbeat_r (stat_client_main_t * sm);
Ole Troan73202102018-08-31 00:29:48 +020082int stat_segment_vec_len(void *vec);
83uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
Ole Troancbb8f582019-04-15 08:53:46 +020084char *stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm);
Ole Troanb63dbc52019-06-14 10:26:14 +020085uint64_t stat_segment_version(void);
86uint64_t stat_segment_version_r(stat_client_main_t *sm);
Ole Troancbb8f582019-04-15 08:53:46 +020087void free(void *ptr);
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040088""") # noqa: E501
Ole Troan73202102018-08-31 00:29:48 +020089
90
91# Utility functions
92def make_string_vector(api, strings):
93 vec = ffi.NULL
94 if type(strings) is not list:
95 strings = [strings]
96 for s in strings:
Ole Troan31555a32018-10-22 09:30:26 +020097 vec = api.stat_segment_string_vector(vec, ffi.new("char []",
Paul Vinciguerra3d04e332019-03-19 17:32:39 -070098 s.encode('utf-8')))
Ole Troan73202102018-08-31 00:29:48 +020099 return vec
100
101
102def make_string_list(api, vec):
103 vec_len = api.stat_segment_vec_len(vec)
104 return [ffi.string(vec[i]) for i in range(vec_len)]
105
106
107# 2-dimensonal array of thread, index
108def simple_counter_vec_list(api, e):
109 vec = []
110 for thread in range(api.stat_segment_vec_len(e)):
111 len_interfaces = api.stat_segment_vec_len(e[thread])
112 if_per_thread = [e[thread][interfaces]
113 for interfaces in range(len_interfaces)]
114 vec.append(if_per_thread)
115 return vec
116
117
118def vlib_counter_dict(c):
119 return {'packets': c.packets,
120 'bytes': c.bytes}
121
122
123def combined_counter_vec_list(api, e):
124 vec = []
125 for thread in range(api.stat_segment_vec_len(e)):
126 len_interfaces = api.stat_segment_vec_len(e[thread])
127 if_per_thread = [vlib_counter_dict(e[thread][interfaces])
128 for interfaces in range(len_interfaces)]
129 vec.append(if_per_thread)
130 return vec
131
Paul Vinciguerraae8819f2019-06-07 13:35:37 -0400132
Ole Troan233e4682019-05-16 15:01:34 +0200133def error_vec_list(api, e):
134 vec = []
135 for thread in range(api.stat_segment_vec_len(e)):
136 vec.append(e[thread])
137 return vec
138
Paul Vinciguerraae8819f2019-06-07 13:35:37 -0400139
Ole Troancbb8f582019-04-15 08:53:46 +0200140def name_vec_list(api, e):
Ole Troan85465582019-04-30 10:04:36 +0200141 return [ffi.string(e[i]).decode('utf-8') for i in
142 range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
143
Ole Troan73202102018-08-31 00:29:48 +0200144
145def stat_entry_to_python(api, e):
Ole Troan58492a82018-09-04 13:19:12 +0200146 # Scalar index
147 if e.type == 1:
148 return e.scalar_value
Ole Troan58492a82018-09-04 13:19:12 +0200149 if e.type == 2:
150 return simple_counter_vec_list(api, e.simple_counter_vec)
Ole Troan73202102018-08-31 00:29:48 +0200151 if e.type == 3:
Ole Troan73202102018-08-31 00:29:48 +0200152 return combined_counter_vec_list(api, e.combined_counter_vec)
Ole Troan58492a82018-09-04 13:19:12 +0200153 if e.type == 4:
Ole Troan233e4682019-05-16 15:01:34 +0200154 return error_vec_list(api, e.error_vector)
Ole Troancbb8f582019-04-15 08:53:46 +0200155 if e.type == 5:
156 return name_vec_list(api, e.name_vector)
157 raise NotImplementedError()
Ole Troan73202102018-08-31 00:29:48 +0200158
159
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800160class VPPStatsIOError(IOError):
Paul Vinciguerra356824f2019-01-05 19:37:27 -0800161 message = "Stat segment client connection returned: " \
162 "%(retval)s %(strerror)s."
163
164 strerror = {-1: "Stat client couldn't open socket",
165 -2: "Stat client socket open but couldn't connect",
166 -3: "Receiving file descriptor failed",
167 -4: "mmap fstat failed",
168 -5: "mmap map failed"
169 }
170
171 def __init__(self, message=None, **kwargs):
172 if 'retval' in kwargs:
173 self.retval = kwargs['retval']
174 kwargs['strerror'] = self.strerror[int(self.retval)]
175
176 if not message:
177 try:
178 message = self.message % kwargs
Paul Vinciguerraae8819f2019-06-07 13:35:37 -0400179 except Exception:
Paul Vinciguerra356824f2019-01-05 19:37:27 -0800180 message = self.message
181 else:
182 message = message % kwargs
183
184 super(VPPStatsIOError, self).__init__(message)
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800185
186
187class VPPStatsClientLoadError(RuntimeError):
188 pass
189
190
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800191class VPPStats(object):
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800192 VPPStatsIOError = VPPStatsIOError
193
YohanPipereau71dd9d52019-06-06 16:34:14 +0200194 default_socketname = '/run/vpp/stats.sock'
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800195 sharedlib_name = 'libvppapiclient.so'
196
197 def __init__(self, socketname=default_socketname, timeout=10):
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500198 self.socketname = socketname
199 self.timeout = timeout
200 self.connected = False
Paul Vinciguerra56421092018-11-21 16:34:09 -0800201 try:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800202 self.api = ffi.dlopen(VPPStats.sharedlib_name)
Paul Vinciguerra56421092018-11-21 16:34:09 -0800203 except Exception:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800204 raise VPPStatsClientLoadError("Could not open: %s" %
205 VPPStats.sharedlib_name)
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500206
207 def connect(self):
Paul Vinciguerra56421092018-11-21 16:34:09 -0800208 self.client = self.api.stat_client_get()
209
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500210 poll_end_time = time.time() + self.timeout
Paul Vinciguerra56421092018-11-21 16:34:09 -0800211 while time.time() < poll_end_time:
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500212 rv = self.api.stat_segment_connect_r(
213 self.socketname.encode('utf-8'), self.client)
Ole Troanc424de72019-06-04 12:33:32 +0200214 # Break out if success or any other error than "no such file"
215 # (indicating that VPP hasn't started yet)
216 if rv == 0 or ffi.errno != 2:
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500217 self.connected = True
Paul Vinciguerra56421092018-11-21 16:34:09 -0800218 break
219
Ole Troan73202102018-08-31 00:29:48 +0200220 if rv != 0:
Paul Vinciguerra356824f2019-01-05 19:37:27 -0800221 raise VPPStatsIOError(retval=rv)
Ole Troan73202102018-08-31 00:29:48 +0200222
223 def heartbeat(self):
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500224 if not self.connected:
225 self.connect()
Paul Vinciguerra56421092018-11-21 16:34:09 -0800226 return self.api.stat_segment_heartbeat_r(self.client)
Ole Troan73202102018-08-31 00:29:48 +0200227
228 def ls(self, patterns):
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500229 if not self.connected:
230 self.connect()
Paul Vinciguerra56421092018-11-21 16:34:09 -0800231 return self.api.stat_segment_ls_r(make_string_vector(self.api,
232 patterns),
233 self.client)
Ole Troan73202102018-08-31 00:29:48 +0200234
Ole Troancbb8f582019-04-15 08:53:46 +0200235 def lsstr(self, patterns):
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500236 if not self.connected:
237 self.connect()
Ole Troancbb8f582019-04-15 08:53:46 +0200238 rv = self.api.stat_segment_ls_r(make_string_vector(self.api,
239 patterns),
240 self.client)
241
242 if rv == ffi.NULL:
243 raise VPPStatsIOError()
Ole Troan85465582019-04-30 10:04:36 +0200244 return [ffi.string(self.api.stat_segment_index_to_name_r(
245 rv[i], self.client)).decode('utf-8')
Ole Troancbb8f582019-04-15 08:53:46 +0200246 for i in range(self.api.stat_segment_vec_len(rv))]
247
Ole Troan73202102018-08-31 00:29:48 +0200248 def dump(self, counters):
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500249 if not self.connected:
250 self.connect()
Ole Troan73202102018-08-31 00:29:48 +0200251 stats = {}
Paul Vinciguerra56421092018-11-21 16:34:09 -0800252 rv = self.api.stat_segment_dump_r(counters, self.client)
Ole Troanb4603a72018-09-18 11:06:33 +0200253 # Raise exception and retry
254 if rv == ffi.NULL:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800255 raise VPPStatsIOError()
Ole Troan73202102018-08-31 00:29:48 +0200256 rv_len = self.api.stat_segment_vec_len(rv)
Ole Troancbb8f582019-04-15 08:53:46 +0200257
Ole Troan73202102018-08-31 00:29:48 +0200258 for i in range(rv_len):
Paul Vinciguerra3d04e332019-03-19 17:32:39 -0700259 n = ffi.string(rv[i].name).decode('utf-8')
Ole Troan73202102018-08-31 00:29:48 +0200260 e = stat_entry_to_python(self.api, rv[i])
Ole Troan31555a32018-10-22 09:30:26 +0200261 if e is not None:
Ole Troan282093f2018-09-19 12:38:51 +0200262 stats[n] = e
Ole Troan73202102018-08-31 00:29:48 +0200263 return stats
264
Ole Troan58492a82018-09-04 13:19:12 +0200265 def get_counter(self, name):
Ole Troanb4603a72018-09-18 11:06:33 +0200266 retries = 0
267 while True:
268 try:
Ole Troan7f991832018-12-06 17:35:12 +0100269 d = self.ls(name)
270 s = self.dump(d)
271 if len(s) > 1:
272 raise AttributeError('Matches multiple counters {}'
273 .format(name))
274 k, v = s.popitem()
275 return v
Paul Vinciguerraae8819f2019-06-07 13:35:37 -0400276 except VPPStatsIOError:
Ole Troanb4603a72018-09-18 11:06:33 +0200277 if retries > 10:
278 return None
279 retries += 1
Ole Troan73202102018-08-31 00:29:48 +0200280
Ole Troan233e4682019-05-16 15:01:34 +0200281 def get_err_counter(self, name):
282 """Get an error counter. The errors from each worker thread
283 are summed"""
284 return sum(self.get_counter(name))
285
Ole Troan73202102018-08-31 00:29:48 +0200286 def disconnect(self):
Paul Vinciguerrae090f4d2019-11-29 17:41:20 -0500287 try:
288 self.api.stat_segment_disconnect_r(self.client)
289 self.api.stat_client_free(self.client)
290 self.connected = False
291 del self.client
292 except AttributeError:
293 # no need to disconnect if we're not connected
294 pass
Ole Troan73202102018-08-31 00:29:48 +0200295
296 def set_errors(self):
297 '''Return all errors counters > 0'''
Ole Troanb4603a72018-09-18 11:06:33 +0200298 retries = 0
299 while True:
300 try:
301 error_names = self.ls(['/err/'])
302 error_counters = self.dump(error_names)
303 break
Paul Vinciguerraae8819f2019-06-07 13:35:37 -0400304 except VPPStatsIOError:
Ole Troanb4603a72018-09-18 11:06:33 +0200305 if retries > 10:
306 return None
307 retries += 1
Paul Vinciguerra56421092018-11-21 16:34:09 -0800308
Ole Troan233e4682019-05-16 15:01:34 +0200309 return {k: sum(error_counters[k])
310 for k in error_counters.keys() if sum(error_counters[k])}
Ole Troan73202102018-08-31 00:29:48 +0200311
312 def set_errors_str(self):
313 '''Return all errors counters > 0 pretty printed'''
Paul Vinciguerra5770c912019-06-29 08:33:05 -0400314 s = ['ERRORS:']
Ole Troan73202102018-08-31 00:29:48 +0200315 error_counters = self.set_errors()
316 for k in sorted(error_counters):
Paul Vinciguerra5770c912019-06-29 08:33:05 -0400317 s.append('{:<60}{:>10}'.format(k, error_counters[k]))
318 return '%s\n' % '\n'.join(s)