tests: use socket transport instead of shared memory
[vpp.git] / src / vpp-api / python / vpp_papi / vpp_stats.py
index 86a80dd..4b7a9bc 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 from __future__ import print_function
 from cffi import FFI
@@ -25,11 +25,10 @@ typedef struct
 {
   stat_directory_type_t type;
   union {
-    uint64_t offset;
     uint64_t index;
     uint64_t value;
+    uint64_t *data;
   };
-  uint64_t offset_vector;
   char name[128]; // TODO change this to pointer to "somewhere"
 } stat_segment_directory_entry_t;
 
@@ -49,11 +48,12 @@ typedef struct
 
 typedef struct
 {
+  uint64_t version;
+  void *base;
   uint64_t epoch;
   uint64_t in_progress;
-  uint64_t directory_offset;
-  uint64_t error_offset;
-  uint64_t stats_offset;
+  stat_segment_directory_entry_t *directory_vector;
+  uint64_t **error_vector;
 } stat_segment_shared_header_t;
 
 typedef struct
@@ -73,7 +73,8 @@ void stat_segment_disconnect (void);
 
 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_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);
 
@@ -81,8 +82,11 @@ double stat_segment_heartbeat_r (stat_client_main_t * sm);
 int stat_segment_vec_len(void *vec);
 uint8_t **stat_segment_string_vector(uint8_t **string_vector, char *string);
 char *stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm);
+uint64_t stat_segment_version(void);
+uint64_t stat_segment_version_r(stat_client_main_t *sm);
 void free(void *ptr);
-""")
+void vac_mem_init (size_t size);
+""")  # noqa: E501
 
 
 # Utility functions
@@ -126,14 +130,18 @@ def combined_counter_vec_list(api, e):
         vec.append(if_per_thread)
     return vec
 
+
 def error_vec_list(api, e):
     vec = []
     for thread in range(api.stat_segment_vec_len(e)):
         vec.append(e[thread])
     return vec
 
+
 def name_vec_list(api, e):
-    return [ffi.string(e[i]).decode('utf-8') for i in range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
+    return [ffi.string(e[i]).decode('utf-8') for i in
+            range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
+
 
 def stat_entry_to_python(api, e):
     # Scalar index
@@ -169,7 +177,7 @@ class VPPStatsIOError(IOError):
         if not message:
             try:
                 message = self.message % kwargs
-            except Exception as e:
+            except Exception:
                 message = self.message
         else:
             message = message % kwargs
@@ -181,49 +189,67 @@ class VPPStatsClientLoadError(RuntimeError):
     pass
 
 
-class VPPStats(object):
+class VPPStats:
     VPPStatsIOError = VPPStatsIOError
 
-    default_socketname = '/var/run/vpp/stats.sock'
+    default_socketname = '/run/vpp/stats.sock'
     sharedlib_name = 'libvppapiclient.so'
 
     def __init__(self, socketname=default_socketname, timeout=10):
+        self.socketname = socketname
+        self.timeout = timeout
+        self.connected = False
         try:
             self.api = ffi.dlopen(VPPStats.sharedlib_name)
         except Exception:
             raise VPPStatsClientLoadError("Could not open: %s" %
                                           VPPStats.sharedlib_name)
+        self.api.vac_mem_init(0)
+
+    def connect(self):
         self.client = self.api.stat_client_get()
 
-        poll_end_time = time.time() + timeout
+        poll_end_time = time.time() + self.timeout
         while time.time() < poll_end_time:
-            rv = self.api.stat_segment_connect_r(socketname.encode('utf-8'),
-                                                 self.client)
-            if rv == 0:
+            rv = self.api.stat_segment_connect_r(
+                self.socketname.encode('utf-8'), self.client)
+            # Break out if success or any other error than "no such file"
+            # (indicating that VPP hasn't started yet)
+            if rv == 0 or ffi.errno != 2:
+                self.connected = True
                 break
 
         if rv != 0:
             raise VPPStatsIOError(retval=rv)
 
     def heartbeat(self):
+        if not self.connected:
+            self.connect()
         return self.api.stat_segment_heartbeat_r(self.client)
 
     def ls(self, patterns):
+        if not self.connected:
+            self.connect()
         return self.api.stat_segment_ls_r(make_string_vector(self.api,
                                                              patterns),
                                           self.client)
 
     def lsstr(self, patterns):
+        if not self.connected:
+            self.connect()
         rv = self.api.stat_segment_ls_r(make_string_vector(self.api,
                                                            patterns),
                                         self.client)
 
         if rv == ffi.NULL:
             raise VPPStatsIOError()
-        return [ffi.string(self.api.stat_segment_index_to_name_r(rv[i], self.client)).decode('utf-8')
+        return [ffi.string(self.api.stat_segment_index_to_name_r(
+            rv[i], self.client)).decode('utf-8')
                 for i in range(self.api.stat_segment_vec_len(rv))]
 
     def dump(self, counters):
+        if not self.connected:
+            self.connect()
         stats = {}
         rv = self.api.stat_segment_dump_r(counters, self.client)
         # Raise exception and retry
@@ -249,7 +275,7 @@ class VPPStats(object):
                                          .format(name))
                 k, v = s.popitem()
                 return v
-            except VPPStatsIOError as e:
+            except VPPStatsIOError:
                 if retries > 10:
                     return None
                 retries += 1
@@ -260,8 +286,14 @@ class VPPStats(object):
         return sum(self.get_counter(name))
 
     def disconnect(self):
-        self.api.stat_segment_disconnect_r(self.client)
-        self.api.stat_client_free(self.client)
+        try:
+            self.api.stat_segment_disconnect_r(self.client)
+            self.api.stat_client_free(self.client)
+            self.connected = False
+            del self.client
+        except AttributeError:
+            # no need to disconnect if we're not connected
+            pass
 
     def set_errors(self):
         '''Return all errors counters > 0'''
@@ -271,7 +303,7 @@ class VPPStats(object):
                 error_names = self.ls(['/err/'])
                 error_counters = self.dump(error_names)
                 break
-            except VPPStatsIOError as e:
+            except VPPStatsIOError:
                 if retries > 10:
                     return None
                 retries += 1
@@ -281,8 +313,8 @@ class VPPStats(object):
 
     def set_errors_str(self):
         '''Return all errors counters > 0 pretty printed'''
-        s = 'ERRORS:\n'
+        s = ['ERRORS:']
         error_counters = self.set_errors()
         for k in sorted(error_counters):
-            s += '{:<60}{:>10}\n'.format(k, error_counters[k])
-        return s
+            s.append('{:<60}{:>10}'.format(k, error_counters[k]))
+        return '%s\n' % '\n'.join(s)