IPIP and IPv6 fragmentation
[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 []", s.encode()))
66     return vec
67
68
69 def make_string_list(api, vec):
70     vec_len = api.stat_segment_vec_len(vec)
71     return [ffi.string(vec[i]) for i in range(vec_len)]
72
73
74 # 2-dimensonal array of thread, index
75 def simple_counter_vec_list(api, e):
76     vec = []
77     for thread in range(api.stat_segment_vec_len(e)):
78         len_interfaces = api.stat_segment_vec_len(e[thread])
79         if_per_thread = [e[thread][interfaces]
80                          for interfaces in range(len_interfaces)]
81         vec.append(if_per_thread)
82     return vec
83
84
85 def vlib_counter_dict(c):
86     return {'packets': c.packets,
87             'bytes': c.bytes}
88
89
90 def combined_counter_vec_list(api, e):
91     vec = []
92     for thread in range(api.stat_segment_vec_len(e)):
93         len_interfaces = api.stat_segment_vec_len(e[thread])
94         if_per_thread = [vlib_counter_dict(e[thread][interfaces])
95                          for interfaces in range(len_interfaces)]
96         vec.append(if_per_thread)
97     return vec
98
99
100 def stat_entry_to_python(api, e):
101     # Scalar index
102     if e.type == 1:
103         return e.scalar_value
104         return None
105     if e.type == 2:
106         return simple_counter_vec_list(api, e.simple_counter_vec)
107     if e.type == 3:
108         return combined_counter_vec_list(api, e.combined_counter_vec)
109     if e.type == 4:
110         return e.error_value
111     return None
112
113
114 class VPPStats:
115     def __init__(self, socketname='/var/run/stats.sock'):
116         self.api = ffi.dlopen('libvppapiclient.so')
117         rv = self.api.stat_segment_connect(socketname.encode())
118         if rv != 0:
119             raise IOError()
120
121     def heartbeat(self):
122         return self.api.stat_segment_heartbeat()
123
124     def ls(self, patterns):
125         return self.api.stat_segment_ls(make_string_vector(self.api, patterns))
126
127     def dump(self, counters):
128         stats = {}
129         rv = self.api.stat_segment_dump(counters)
130         # Raise exception and retry
131         if rv == ffi.NULL:
132             raise IOError()
133         rv_len = self.api.stat_segment_vec_len(rv)
134         for i in range(rv_len):
135             n = ffi.string(rv[i].name).decode()
136             e = stat_entry_to_python(self.api, rv[i])
137             if e != None:
138                 stats[n] = e
139         return stats
140
141     def get_counter(self, name):
142         retries = 0
143         while True:
144             try:
145                 dir = self.ls(name)
146                 return self.dump(dir).values()[0]
147             except:
148                 if retries > 10:
149                     return None
150                 retries += 1
151                 pass
152
153     def disconnect(self):
154         self.api.stat_segment_disconnect()
155
156     def set_errors(self):
157         '''Return all errors counters > 0'''
158         retries = 0
159         while True:
160             try:
161                 error_names = self.ls(['/err/'])
162                 error_counters = self.dump(error_names)
163                 break
164             except:
165                 if retries > 10:
166                     return None
167                 retries += 1
168                 pass
169         return {k: error_counters[k]
170                 for k in error_counters.keys() if error_counters[k]}
171
172     def set_errors_str(self):
173         '''Return all errors counters > 0 pretty printed'''
174         s = 'ERRORS:\n'
175         error_counters = self.set_errors()
176         for k in sorted(error_counters):
177             s += '{:<60}{:>10}\n'.format(k, error_counters[k])
178         return s