3 from __future__ import print_function
9 typedef uint64_t counter_t;
16 STAT_DIR_TYPE_ILLEGAL = 0,
17 STAT_DIR_TYPE_SCALAR_INDEX,
18 STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
19 STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
20 STAT_DIR_TYPE_ERROR_INDEX,
21 STAT_DIR_TYPE_NAME_VECTOR,
22 } stat_directory_type_t;
26 stat_directory_type_t type;
32 char name[128]; // TODO change this to pointer to "somewhere"
33 } stat_segment_directory_entry_t;
38 stat_directory_type_t type;
42 counter_t *error_vector;
43 counter_t **simple_counter_vec;
44 vlib_counter_t **combined_counter_vec;
45 uint8_t **name_vector;
47 } stat_segment_data_t;
55 stat_segment_directory_entry_t *directory_vector;
56 uint64_t **error_vector;
57 } stat_segment_shared_header_t;
61 uint64_t current_epoch;
62 stat_segment_shared_header_t *shared_header;
63 stat_segment_directory_entry_t *directory_vector;
67 stat_client_main_t * stat_client_get(void);
68 void stat_client_free(stat_client_main_t * sm);
69 int stat_segment_connect_r (char *socket_name, stat_client_main_t * sm);
70 int stat_segment_connect (char *socket_name);
71 void stat_segment_disconnect_r (stat_client_main_t * sm);
72 void stat_segment_disconnect (void);
74 uint32_t *stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm);
75 uint32_t *stat_segment_ls (uint8_t ** pattern);
76 stat_segment_data_t *stat_segment_dump_r (uint32_t * stats,
77 stat_client_main_t * sm);
78 stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec);
79 void stat_segment_data_free (stat_segment_data_t * res);
81 double stat_segment_heartbeat_r (stat_client_main_t * sm);
82 int stat_segment_vec_len(void *vec);
83 uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
84 char *stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm);
85 uint64_t stat_segment_version(void);
86 uint64_t stat_segment_version_r(stat_client_main_t *sm);
88 void vac_mem_init (size_t size);
93 def make_string_vector(api, strings):
95 if type(strings) is not list:
98 vec = api.stat_segment_string_vector(vec, ffi.new("char []",
103 def make_string_list(api, vec):
104 vec_len = api.stat_segment_vec_len(vec)
105 return [ffi.string(vec[i]) for i in range(vec_len)]
108 # 2-dimensonal array of thread, index
109 def simple_counter_vec_list(api, e):
111 for thread in range(api.stat_segment_vec_len(e)):
112 len_interfaces = api.stat_segment_vec_len(e[thread])
113 if_per_thread = [e[thread][interfaces]
114 for interfaces in range(len_interfaces)]
115 vec.append(if_per_thread)
119 def vlib_counter_dict(c):
120 return {'packets': c.packets,
124 def combined_counter_vec_list(api, e):
126 for thread in range(api.stat_segment_vec_len(e)):
127 len_interfaces = api.stat_segment_vec_len(e[thread])
128 if_per_thread = [vlib_counter_dict(e[thread][interfaces])
129 for interfaces in range(len_interfaces)]
130 vec.append(if_per_thread)
134 def error_vec_list(api, e):
136 for thread in range(api.stat_segment_vec_len(e)):
137 vec.append(e[thread])
141 def name_vec_list(api, e):
142 return [ffi.string(e[i]).decode('utf-8') for i in
143 range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
146 def stat_entry_to_python(api, e):
149 return e.scalar_value
151 return simple_counter_vec_list(api, e.simple_counter_vec)
153 return combined_counter_vec_list(api, e.combined_counter_vec)
155 return error_vec_list(api, e.error_vector)
157 return name_vec_list(api, e.name_vector)
158 raise NotImplementedError()
161 class VPPStatsIOError(IOError):
162 message = "Stat segment client connection returned: " \
163 "%(retval)s %(strerror)s."
165 strerror = {-1: "Stat client couldn't open socket",
166 -2: "Stat client socket open but couldn't connect",
167 -3: "Receiving file descriptor failed",
168 -4: "mmap fstat failed",
169 -5: "mmap map failed"
172 def __init__(self, message=None, **kwargs):
173 if 'retval' in kwargs:
174 self.retval = kwargs['retval']
175 kwargs['strerror'] = self.strerror[int(self.retval)]
179 message = self.message % kwargs
181 message = self.message
183 message = message % kwargs
185 super(VPPStatsIOError, self).__init__(message)
188 class VPPStatsClientLoadError(RuntimeError):
192 class VPPStats(object):
193 VPPStatsIOError = VPPStatsIOError
195 default_socketname = '/run/vpp/stats.sock'
196 sharedlib_name = 'libvppapiclient.so'
198 def __init__(self, socketname=default_socketname, timeout=10):
199 self.socketname = socketname
200 self.timeout = timeout
201 self.connected = False
203 self.api = ffi.dlopen(VPPStats.sharedlib_name)
205 raise VPPStatsClientLoadError("Could not open: %s" %
206 VPPStats.sharedlib_name)
207 self.api.vac_mem_init(0)
210 self.client = self.api.stat_client_get()
212 poll_end_time = time.time() + self.timeout
213 while time.time() < poll_end_time:
214 rv = self.api.stat_segment_connect_r(
215 self.socketname.encode('utf-8'), self.client)
216 # Break out if success or any other error than "no such file"
217 # (indicating that VPP hasn't started yet)
218 if rv == 0 or ffi.errno != 2:
219 self.connected = True
223 raise VPPStatsIOError(retval=rv)
226 if not self.connected:
228 return self.api.stat_segment_heartbeat_r(self.client)
230 def ls(self, patterns):
231 if not self.connected:
233 return self.api.stat_segment_ls_r(make_string_vector(self.api,
237 def lsstr(self, patterns):
238 if not self.connected:
240 rv = self.api.stat_segment_ls_r(make_string_vector(self.api,
245 raise VPPStatsIOError()
246 return [ffi.string(self.api.stat_segment_index_to_name_r(
247 rv[i], self.client)).decode('utf-8')
248 for i in range(self.api.stat_segment_vec_len(rv))]
250 def dump(self, counters):
251 if not self.connected:
254 rv = self.api.stat_segment_dump_r(counters, self.client)
255 # Raise exception and retry
257 raise VPPStatsIOError()
258 rv_len = self.api.stat_segment_vec_len(rv)
260 for i in range(rv_len):
261 n = ffi.string(rv[i].name).decode('utf-8')
262 e = stat_entry_to_python(self.api, rv[i])
267 def get_counter(self, name):
274 raise AttributeError('Matches multiple counters {}'
278 except VPPStatsIOError:
283 def get_err_counter(self, name):
284 """Get an error counter. The errors from each worker thread
286 return sum(self.get_counter(name))
288 def disconnect(self):
290 self.api.stat_segment_disconnect_r(self.client)
291 self.api.stat_client_free(self.client)
292 self.connected = False
294 except AttributeError:
295 # no need to disconnect if we're not connected
298 def set_errors(self):
299 '''Return all errors counters > 0'''
303 error_names = self.ls(['/err/'])
304 error_counters = self.dump(error_names)
306 except VPPStatsIOError:
311 return {k: sum(error_counters[k])
312 for k in error_counters.keys() if sum(error_counters[k])}
314 def set_errors_str(self):
315 '''Return all errors counters > 0 pretty printed'''
317 error_counters = self.set_errors()
318 for k in sorted(error_counters):
319 s.append('{:<60}{:>10}'.format(k, error_counters[k]))
320 return '%s\n' % '\n'.join(s)