stat_client issues while running parallel tests.

This patch addresses intermittent problems we saw in our CI while running parallel tests.

Change-Id: Icb5fdb34cc134e3eb341225d56ab67fbbef80b0d
Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
diff --git a/src/vpp-api/python/vpp_papi/vpp_stats.py b/src/vpp-api/python/vpp_papi/vpp_stats.py
index 76e1e54..654cb82 100644
--- a/src/vpp-api/python/vpp_papi/vpp_stats.py
+++ b/src/vpp-api/python/vpp_papi/vpp_stats.py
@@ -2,6 +2,7 @@
 
 from __future__ import print_function
 from cffi import FFI
+import time
 
 ffi = FFI()
 ffi.cdef("""
@@ -44,12 +45,37 @@
   };
 } stat_segment_data_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);
 
+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);
@@ -113,21 +139,34 @@
 
 
 class VPPStats:
-    def __init__(self, socketname='/var/run/stats.sock'):
-        self.api = ffi.dlopen('libvppapiclient.so')
-        rv = self.api.stat_segment_connect(socketname.encode())
+    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()
@@ -149,10 +188,10 @@
                 if retries > 10:
                     return None
                 retries += 1
-                pass
 
     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'''
@@ -166,7 +205,7 @@
                 if retries > 10:
                     return None
                 retries += 1
-                pass
+
         return {k: error_counters[k]
                 for k in error_counters.keys() if error_counters[k]}