blob: c90aa349434c6a303ee856e13df5fc4bf93c7757 [file] [log] [blame]
Ole Troan73202102018-08-31 00:29:48 +02001#!/usr/bin/env python
2
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 offset;
29 uint64_t index;
30 uint64_t value;
31 };
32 uint64_t offset_vector;
33 char name[128]; // TODO change this to pointer to "somewhere"
34} stat_segment_directory_entry_t;
35
36typedef struct
37{
38 char *name;
39 stat_directory_type_t type;
40 union
41 {
Ole Troan73202102018-08-31 00:29:48 +020042 double scalar_value;
43 uint64_t error_value;
Ole Troan73202102018-08-31 00:29:48 +020044 counter_t **simple_counter_vec;
45 vlib_counter_t **combined_counter_vec;
Ole Troancbb8f582019-04-15 08:53:46 +020046 uint8_t **name_vector;
Ole Troan73202102018-08-31 00:29:48 +020047 };
48} stat_segment_data_t;
49
Paul Vinciguerra56421092018-11-21 16:34:09 -080050typedef struct
51{
52 uint64_t epoch;
53 uint64_t in_progress;
54 uint64_t directory_offset;
55 uint64_t error_offset;
56 uint64_t stats_offset;
57} 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);
Paul Vinciguerra56421092018-11-21 16:34:09 -080076stat_segment_data_t *stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm);
Ole Troan58492a82018-09-04 13:19:12 +020077stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec);
Ole Troan73202102018-08-31 00:29:48 +020078void stat_segment_data_free (stat_segment_data_t * res);
Paul Vinciguerra56421092018-11-21 16:34:09 -080079
80double stat_segment_heartbeat_r (stat_client_main_t * sm);
Ole Troan73202102018-08-31 00:29:48 +020081int stat_segment_vec_len(void *vec);
82uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
Ole Troancbb8f582019-04-15 08:53:46 +020083char *stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm);
84void free(void *ptr);
Ole Troan73202102018-08-31 00:29:48 +020085""")
86
87
88# Utility functions
89def make_string_vector(api, strings):
90 vec = ffi.NULL
91 if type(strings) is not list:
92 strings = [strings]
93 for s in strings:
Ole Troan31555a32018-10-22 09:30:26 +020094 vec = api.stat_segment_string_vector(vec, ffi.new("char []",
Paul Vinciguerra3d04e332019-03-19 17:32:39 -070095 s.encode('utf-8')))
Ole Troan73202102018-08-31 00:29:48 +020096 return vec
97
98
99def make_string_list(api, vec):
100 vec_len = api.stat_segment_vec_len(vec)
101 return [ffi.string(vec[i]) for i in range(vec_len)]
102
103
104# 2-dimensonal array of thread, index
105def simple_counter_vec_list(api, e):
106 vec = []
107 for thread in range(api.stat_segment_vec_len(e)):
108 len_interfaces = api.stat_segment_vec_len(e[thread])
109 if_per_thread = [e[thread][interfaces]
110 for interfaces in range(len_interfaces)]
111 vec.append(if_per_thread)
112 return vec
113
114
115def vlib_counter_dict(c):
116 return {'packets': c.packets,
117 'bytes': c.bytes}
118
119
120def combined_counter_vec_list(api, e):
121 vec = []
122 for thread in range(api.stat_segment_vec_len(e)):
123 len_interfaces = api.stat_segment_vec_len(e[thread])
124 if_per_thread = [vlib_counter_dict(e[thread][interfaces])
125 for interfaces in range(len_interfaces)]
126 vec.append(if_per_thread)
127 return vec
128
Ole Troancbb8f582019-04-15 08:53:46 +0200129def name_vec_list(api, e):
130 return [ffi.string(e[i]).decode('utf-8') for i in range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
Ole Troan73202102018-08-31 00:29:48 +0200131
132def stat_entry_to_python(api, e):
Ole Troan58492a82018-09-04 13:19:12 +0200133 # Scalar index
134 if e.type == 1:
135 return e.scalar_value
Ole Troan58492a82018-09-04 13:19:12 +0200136 if e.type == 2:
137 return simple_counter_vec_list(api, e.simple_counter_vec)
Ole Troan73202102018-08-31 00:29:48 +0200138 if e.type == 3:
Ole Troan73202102018-08-31 00:29:48 +0200139 return combined_counter_vec_list(api, e.combined_counter_vec)
Ole Troan58492a82018-09-04 13:19:12 +0200140 if e.type == 4:
Ole Troan73202102018-08-31 00:29:48 +0200141 return e.error_value
Ole Troancbb8f582019-04-15 08:53:46 +0200142 if e.type == 5:
143 return name_vec_list(api, e.name_vector)
144 raise NotImplementedError()
Ole Troan73202102018-08-31 00:29:48 +0200145
146
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800147class VPPStatsIOError(IOError):
Paul Vinciguerra356824f2019-01-05 19:37:27 -0800148 message = "Stat segment client connection returned: " \
149 "%(retval)s %(strerror)s."
150
151 strerror = {-1: "Stat client couldn't open socket",
152 -2: "Stat client socket open but couldn't connect",
153 -3: "Receiving file descriptor failed",
154 -4: "mmap fstat failed",
155 -5: "mmap map failed"
156 }
157
158 def __init__(self, message=None, **kwargs):
159 if 'retval' in kwargs:
160 self.retval = kwargs['retval']
161 kwargs['strerror'] = self.strerror[int(self.retval)]
162
163 if not message:
164 try:
165 message = self.message % kwargs
166 except Exception as e:
167 message = self.message
168 else:
169 message = message % kwargs
170
171 super(VPPStatsIOError, self).__init__(message)
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800172
173
174class VPPStatsClientLoadError(RuntimeError):
175 pass
176
177
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800178class VPPStats(object):
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800179 VPPStatsIOError = VPPStatsIOError
180
Ole Troancbb8f582019-04-15 08:53:46 +0200181 default_socketname = '/var/run/vpp/stats.sock'
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800182 sharedlib_name = 'libvppapiclient.so'
183
184 def __init__(self, socketname=default_socketname, timeout=10):
Paul Vinciguerra56421092018-11-21 16:34:09 -0800185 try:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800186 self.api = ffi.dlopen(VPPStats.sharedlib_name)
Paul Vinciguerra56421092018-11-21 16:34:09 -0800187 except Exception:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800188 raise VPPStatsClientLoadError("Could not open: %s" %
189 VPPStats.sharedlib_name)
Paul Vinciguerra56421092018-11-21 16:34:09 -0800190 self.client = self.api.stat_client_get()
191
192 poll_end_time = time.time() + timeout
193 while time.time() < poll_end_time:
Paul Vinciguerra3d04e332019-03-19 17:32:39 -0700194 rv = self.api.stat_segment_connect_r(socketname.encode('utf-8'),
Paul Vinciguerra56421092018-11-21 16:34:09 -0800195 self.client)
196 if rv == 0:
197 break
198
Ole Troan73202102018-08-31 00:29:48 +0200199 if rv != 0:
Paul Vinciguerra356824f2019-01-05 19:37:27 -0800200 raise VPPStatsIOError(retval=rv)
Ole Troan73202102018-08-31 00:29:48 +0200201
202 def heartbeat(self):
Paul Vinciguerra56421092018-11-21 16:34:09 -0800203 return self.api.stat_segment_heartbeat_r(self.client)
Ole Troan73202102018-08-31 00:29:48 +0200204
205 def ls(self, patterns):
Paul Vinciguerra56421092018-11-21 16:34:09 -0800206 return self.api.stat_segment_ls_r(make_string_vector(self.api,
207 patterns),
208 self.client)
Ole Troan73202102018-08-31 00:29:48 +0200209
Ole Troancbb8f582019-04-15 08:53:46 +0200210 def lsstr(self, patterns):
211 rv = self.api.stat_segment_ls_r(make_string_vector(self.api,
212 patterns),
213 self.client)
214
215 if rv == ffi.NULL:
216 raise VPPStatsIOError()
217 return [ffi.string(self.api.stat_segment_index_to_name_r(rv[i], self.client)).decode('utf-8')
218 for i in range(self.api.stat_segment_vec_len(rv))]
219
Ole Troan73202102018-08-31 00:29:48 +0200220 def dump(self, counters):
221 stats = {}
Paul Vinciguerra56421092018-11-21 16:34:09 -0800222 rv = self.api.stat_segment_dump_r(counters, self.client)
Ole Troanb4603a72018-09-18 11:06:33 +0200223 # Raise exception and retry
224 if rv == ffi.NULL:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800225 raise VPPStatsIOError()
Ole Troan73202102018-08-31 00:29:48 +0200226 rv_len = self.api.stat_segment_vec_len(rv)
Ole Troancbb8f582019-04-15 08:53:46 +0200227
Ole Troan73202102018-08-31 00:29:48 +0200228 for i in range(rv_len):
Paul Vinciguerra3d04e332019-03-19 17:32:39 -0700229 n = ffi.string(rv[i].name).decode('utf-8')
Ole Troan73202102018-08-31 00:29:48 +0200230 e = stat_entry_to_python(self.api, rv[i])
Ole Troan31555a32018-10-22 09:30:26 +0200231 if e is not None:
Ole Troan282093f2018-09-19 12:38:51 +0200232 stats[n] = e
Ole Troan73202102018-08-31 00:29:48 +0200233 return stats
234
Ole Troan58492a82018-09-04 13:19:12 +0200235 def get_counter(self, name):
Ole Troanb4603a72018-09-18 11:06:33 +0200236 retries = 0
237 while True:
238 try:
Ole Troan7f991832018-12-06 17:35:12 +0100239 d = self.ls(name)
240 s = self.dump(d)
241 if len(s) > 1:
242 raise AttributeError('Matches multiple counters {}'
243 .format(name))
244 k, v = s.popitem()
245 return v
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800246 except VPPStatsIOError as e:
Ole Troanb4603a72018-09-18 11:06:33 +0200247 if retries > 10:
248 return None
249 retries += 1
Ole Troan73202102018-08-31 00:29:48 +0200250
251 def disconnect(self):
Paul Vinciguerra56421092018-11-21 16:34:09 -0800252 self.api.stat_segment_disconnect_r(self.client)
253 self.api.stat_client_free(self.client)
Ole Troan73202102018-08-31 00:29:48 +0200254
255 def set_errors(self):
256 '''Return all errors counters > 0'''
Ole Troanb4603a72018-09-18 11:06:33 +0200257 retries = 0
258 while True:
259 try:
260 error_names = self.ls(['/err/'])
261 error_counters = self.dump(error_names)
262 break
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800263 except VPPStatsIOError as e:
Ole Troanb4603a72018-09-18 11:06:33 +0200264 if retries > 10:
265 return None
266 retries += 1
Paul Vinciguerra56421092018-11-21 16:34:09 -0800267
Ole Troan73202102018-08-31 00:29:48 +0200268 return {k: error_counters[k]
269 for k in error_counters.keys() if error_counters[k]}
270
271 def set_errors_str(self):
272 '''Return all errors counters > 0 pretty printed'''
273 s = 'ERRORS:\n'
274 error_counters = self.set_errors()
275 for k in sorted(error_counters):
276 s += '{:<60}{:>10}\n'.format(k, error_counters[k])
277 return s