API: Add support for type aliases
[vpp.git] / src / vpp-api / python / vpp_papi / vpp_stats.py
index b84a0e5..8344de0 100644 (file)
@@ -2,6 +2,7 @@
 
 from __future__ import print_function
 from cffi import FFI
+import time
 
 ffi = FFI()
 ffi.cdef("""
@@ -13,45 +14,68 @@ typedef struct {
 
 typedef enum {
   STAT_DIR_TYPE_ILLEGAL = 0,
-  STAT_DIR_TYPE_SCALAR_POINTER,
-  STAT_DIR_TYPE_VECTOR_POINTER,
+  STAT_DIR_TYPE_SCALAR_INDEX,
   STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
   STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
   STAT_DIR_TYPE_ERROR_INDEX,
-  STAT_DIR_TYPE_SERIALIZED_NODES,
 } stat_directory_type_t;
 
-typedef struct {
+typedef struct
+{
   stat_directory_type_t type;
-  void *value;
+  union {
+    uint64_t offset;
+    uint64_t index;
+    uint64_t value;
+  };
+  uint64_t offset_vector;
+  char name[128]; // TODO change this to pointer to "somewhere"
 } stat_segment_directory_entry_t;
 
-typedef struct {
+typedef struct
+{
   char *name;
   stat_directory_type_t type;
-  union {
+  union
+  {
     double scalar_value;
     uint64_t error_value;
-    uint64_t *vector_pointer;
     counter_t **simple_counter_vec;
     vlib_counter_t **combined_counter_vec;
   };
 } stat_segment_data_t;
 
-typedef struct {
-  char *name;
-  stat_segment_directory_entry_t *ep;
-} stat_segment_cached_pointer_t;
-
+typedef struct
+{
+  uint64_t epoch;
+  uint64_t in_progress;
+  uint64_t directory_offset;
+  uint64_t error_offset;
+  uint64_t stats_offset;
+} stat_segment_shared_header_t;
+
+typedef struct
+{
+  uint64_t current_epoch;
+  stat_segment_shared_header_t *shared_header;
+  stat_segment_directory_entry_t *directory_vector;
+  ssize_t memory_size;
+} stat_client_main_t;
+
+stat_client_main_t * stat_client_get(void);
+void stat_client_free(stat_client_main_t * sm);
+int stat_segment_connect_r (char *socket_name, stat_client_main_t * sm);
 int stat_segment_connect (char *socket_name);
+void stat_segment_disconnect_r (stat_client_main_t * sm);
 void stat_segment_disconnect (void);
 
-uint8_t  **stat_segment_ls (uint8_t **pattern);
-stat_segment_data_t *stat_segment_dump (uint8_t ** counter_vec);
-/* Collects registered counters */
-stat_segment_cached_pointer_t *stat_segment_register (uint8_t ** counter_vec);
-stat_segment_data_t *stat_segment_collect (stat_segment_cached_pointer_t *);
+uint32_t *stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm);
+uint32_t *stat_segment_ls (uint8_t ** pattern);
+stat_segment_data_t *stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm);
+stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec);
 void stat_segment_data_free (stat_segment_data_t * res);
+
+double stat_segment_heartbeat_r (stat_client_main_t * sm);
 double stat_segment_heartbeat (void);
 int stat_segment_vec_len(void *vec);
 uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
@@ -64,7 +88,8 @@ def make_string_vector(api, strings):
     if type(strings) is not list:
         strings = [strings]
     for s in strings:
-        vec = api.stat_segment_string_vector(vec, ffi.new("char []", s))
+        vec = api.stat_segment_string_vector(vec, ffi.new("char []",
+                                                          s.encode()))
     return vec
 
 
@@ -100,49 +125,87 @@ def combined_counter_vec_list(api, e):
 
 
 def stat_entry_to_python(api, e):
+    # Scalar index
+    if e.type == 1:
+        return e.scalar_value
+        return None
+    if e.type == 2:
+        return simple_counter_vec_list(api, e.simple_counter_vec)
     if e.type == 3:
-        return simple_counter_vec_list(e.simple_counter_vec)
-    if e.type == 4:
         return combined_counter_vec_list(api, e.combined_counter_vec)
-    if e.type == 5:
+    if e.type == 4:
         return e.error_value
     return None
 
 
-class VPPStats:
-    def __init__(self, socketname='/var/run/stats.sock'):
-        self.api = ffi.dlopen('libvppapiclient.so')
-        rv = self.api.stat_segment_connect(socketname)
+class VPPStats(object):
+    def __init__(self, socketname='/var/run/stats.sock', timeout=10):
+        try:
+            self.api = ffi.dlopen('libvppapiclient.so')
+        except Exception:
+            raise RuntimeError("Could not open: libvppapiclient.so")
+        self.client = self.api.stat_client_get()
+
+        poll_end_time = time.time() + timeout
+        while time.time() < poll_end_time:
+            rv = self.api.stat_segment_connect_r(socketname.encode(),
+                                                 self.client)
+            if rv == 0:
+                break
+
         if rv != 0:
             raise IOError()
 
     def heartbeat(self):
-        return self.api.stat_segment_heartbeat()
+        return self.api.stat_segment_heartbeat_r(self.client)
 
     def ls(self, patterns):
-        return self.api.stat_segment_ls(make_string_vector(self.api, patterns))
+        return self.api.stat_segment_ls_r(make_string_vector(self.api,
+                                                             patterns),
+                                          self.client)
 
     def dump(self, counters):
         stats = {}
-        rv = self.api.stat_segment_dump(counters)
+        rv = self.api.stat_segment_dump_r(counters, self.client)
+        # Raise exception and retry
+        if rv == ffi.NULL:
+            raise IOError()
         rv_len = self.api.stat_segment_vec_len(rv)
         for i in range(rv_len):
-            n = ffi.string(rv[i].name)
+            n = ffi.string(rv[i].name).decode()
             e = stat_entry_to_python(self.api, rv[i])
-            if e:
+            if e is not None:
                 stats[n] = e
         return stats
 
-    def dump_str(self, counters_str):
-        return self.dump(make_string_vector(self.api, counters_str))
+    def get_counter(self, name):
+        retries = 0
+        while True:
+            try:
+                dir = self.ls(name)
+                return self.dump(dir).values()[0]
+            except Exception as e:
+                if retries > 10:
+                    return None
+                retries += 1
 
     def disconnect(self):
-        self.api.stat_segment_disconnect()
+        self.api.stat_segment_disconnect_r(self.client)
+        self.api.stat_client_free(self.client)
 
     def set_errors(self):
         '''Return all errors counters > 0'''
-        error_names = self.ls(['/err/'])
-        error_counters = self.dump(error_names)
+        retries = 0
+        while True:
+            try:
+                error_names = self.ls(['/err/'])
+                error_counters = self.dump(error_names)
+                break
+            except Exception as e:
+                if retries > 10:
+                    return None
+                retries += 1
+
         return {k: error_counters[k]
                 for k in error_counters.keys() if error_counters[k]}
 
@@ -153,9 +216,3 @@ class VPPStats:
         for k in sorted(error_counters):
             s += '{:<60}{:>10}\n'.format(k, error_counters[k])
         return s
-
-    def register(self):
-        raise NotImplemented
-
-    def collect(self):
-        raise NotImplemented