76e1e5435f0458b9bb3a314a21979a8f1624efa6
[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_INDEX,
17   STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
18   STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
19   STAT_DIR_TYPE_ERROR_INDEX,
20 } stat_directory_type_t;
21
22 typedef struct
23 {
24   stat_directory_type_t type;
25   union {
26     uint64_t offset;
27     uint64_t index;
28     uint64_t value;
29   };
30   uint64_t offset_vector;
31   char name[128]; // TODO change this to pointer to "somewhere"
32 } stat_segment_directory_entry_t;
33
34 typedef struct
35 {
36   char *name;
37   stat_directory_type_t type;
38   union
39   {
40     double scalar_value;
41     uint64_t error_value;
42     counter_t **simple_counter_vec;
43     vlib_counter_t **combined_counter_vec;
44   };
45 } stat_segment_data_t;
46
47 int stat_segment_connect (char *socket_name);
48 void stat_segment_disconnect (void);
49
50 uint32_t *stat_segment_ls (uint8_t ** pattern);
51 stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec);
52 void stat_segment_data_free (stat_segment_data_t * res);
53 double stat_segment_heartbeat (void);
54 int stat_segment_vec_len(void *vec);
55 uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
56 """)
57
58
59 # Utility functions
60 def make_string_vector(api, strings):
61     vec = ffi.NULL
62     if type(strings) is not list:
63         strings = [strings]
64     for s in strings:
65         vec = api.stat_segment_string_vector(vec, ffi.new("char []",
66                                                           s.encode()))
67     return vec
68
69
70 def make_string_list(api, vec):
71     vec_len = api.stat_segment_vec_len(vec)
72     return [ffi.string(vec[i]) for i in range(vec_len)]
73
74
75 # 2-dimensonal array of thread, index
76 def simple_counter_vec_list(api, e):
77     vec = []
78     for thread in range(api.stat_segment_vec_len(e)):
79         len_interfaces = api.stat_segment_vec_len(e[thread])
80         if_per_thread = [e[thread][interfaces]
81                          for interfaces in range(len_interfaces)]
82         vec.append(if_per_thread)
83     return vec
84
85
86 def vlib_counter_dict(c):
87     return {'packets': c.packets,
88             'bytes': c.bytes}
89
90
91 def combined_counter_vec_list(api, e):
92     vec = []
93     for thread in range(api.stat_segment_vec_len(e)):
94         len_interfaces = api.stat_segment_vec_len(e[thread])
95         if_per_thread = [vlib_counter_dict(e[thread][interfaces])
96                          for interfaces in range(len_interfaces)]
97         vec.append(if_per_thread)
98     return vec
99
100
101 def stat_entry_to_python(api, e):
102     # Scalar index
103     if e.type == 1:
104         return e.scalar_value
105         return None
106     if e.type == 2:
107         return simple_counter_vec_list(api, e.simple_counter_vec)
108     if e.type == 3:
109         return combined_counter_vec_list(api, e.combined_counter_vec)
110     if e.type == 4:
111         return e.error_value
112     return None
113
114
115 class VPPStats:
116     def __init__(self, socketname='/var/run/stats.sock'):
117         self.api = ffi.dlopen('libvppapiclient.so')
118         rv = self.api.stat_segment_connect(socketname.encode())
119         if rv != 0:
120             raise IOError()
121
122     def heartbeat(self):
123         return self.api.stat_segment_heartbeat()
124
125     def ls(self, patterns):
126         return self.api.stat_segment_ls(make_string_vector(self.api, patterns))
127
128     def dump(self, counters):
129         stats = {}
130         rv = self.api.stat_segment_dump(counters)
131         # Raise exception and retry
132         if rv == ffi.NULL:
133             raise IOError()
134         rv_len = self.api.stat_segment_vec_len(rv)
135         for i in range(rv_len):
136             n = ffi.string(rv[i].name).decode()
137             e = stat_entry_to_python(self.api, rv[i])
138             if e is not None:
139                 stats[n] = e
140         return stats
141
142     def get_counter(self, name):
143         retries = 0
144         while True:
145             try:
146                 dir = self.ls(name)
147                 return self.dump(dir).values()[0]
148             except Exception as e:
149                 if retries > 10:
150                     return None
151                 retries += 1
152                 pass
153
154     def disconnect(self):
155         self.api.stat_segment_disconnect()
156
157     def set_errors(self):
158         '''Return all errors counters > 0'''
159         retries = 0
160         while True:
161             try:
162                 error_names = self.ls(['/err/'])
163                 error_counters = self.dump(error_names)
164                 break
165             except Exception as e:
166                 if retries > 10:
167                     return None
168                 retries += 1
169                 pass
170         return {k: error_counters[k]
171                 for k in error_counters.keys() if error_counters[k]}
172
173     def set_errors_str(self):
174         '''Return all errors counters > 0 pretty printed'''
175         s = 'ERRORS:\n'
176         error_counters = self.set_errors()
177         for k in sorted(error_counters):
178             s += '{:<60}{:>10}\n'.format(k, error_counters[k])
179         return s