HC2VPP-229 - Combined honeycomb and vpp restart handling 78/8378/3
authorJan Srnicek <[email protected]>
Mon, 18 Sep 2017 08:21:08 +0000 (10:21 +0200)
committerJan Srnicek <[email protected]>
Mon, 18 Sep 2017 12:04:11 +0000 (12:04 +0000)
Prevents honeycomb from attempting disconnection on different instance
of vpp that it was connected to

Change-Id: I8dedcaebb08ac75dc0fb9568ab66a011b1b1b676
Signed-off-by: Jan Srnicek <[email protected]>
vpp-common/vpp-common-integration/src/main/java/io/fd/hc2vpp/common/integration/JVppRegistryProvider.java
vpp-common/vpp-common-integration/src/test/java/io/fd/hc2vpp/common/integration/VppCommonModuleTest.java

index 47c5128..300e038 100644 (file)
 
 package io.fd.hc2vpp.common.integration;
 
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+import com.google.common.primitives.UnsignedInts;
 import com.google.inject.Inject;
-import io.fd.hc2vpp.common.translate.util.VppStatusListener;
 import io.fd.honeycomb.binding.init.ProviderTrait;
 import io.fd.honeycomb.data.init.ShutdownHandler;
 import io.fd.vpp.jvpp.JVppRegistry;
 import io.fd.vpp.jvpp.JVppRegistryImpl;
-import java.io.IOException;
+import io.fd.vpp.jvpp.VppJNIConnection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
 /**
  * Provides JVppRegistry. Must be a singleton due to shutdown hook usage. Registers shutdown hook to disconnect from
  * VPP.
@@ -37,35 +46,69 @@ public final class JVppRegistryProvider extends ProviderTrait<JVppRegistry> {
     @Inject
     private VppConfigAttributes config;
     @Inject
-    private VppStatusListener vppStatus;
-    @Inject
     private ShutdownHandler shutdownHandler;
 
+    private long connectedVppPid;
+
     @Override
-    protected JVppRegistryImpl create() {
+    protected JVppRegistry create() {
+        final JVppRegistry registry;
         try {
-            final JVppRegistryImpl registry = new JVppRegistryImpl(config.jvppConnectionName);
-
-            // Closing JVpp connection with shutdown hook to erase the connection from VPP so HC will be able
-            // to connect next time. If JVM is force closed, this will not be executed and VPP connection
-            // with name from config will stay open and prevent next startup of HC to success
+            registry = new JVppRegistryImpl(config.jvppConnectionName);
+            connectedVppPid = initConnectedVppPid(registry);
             shutdownHandler.register("jvpp-registry", () -> {
+                // Closing JVpp connection with shutdown hook to erase the connection from VPP so HC will be able
+                // to connect next time. If JVM is force closed, this will not be executed and VPP connection
+                // with name from config will stay open and prevent next startup of HC to success
+
                 LOG.info("Disconnecting from VPP");
-                if (vppStatus.isDown()) {
-                    LOG.info("VPP is down. JVppRegistry cleanup is not needed. Exiting");
-                    return;
-                }
-                try {
+
+                // Handles restart honeycomb service or restart vpp service
+                // this tells whether vpp that was honeycomb connected to is running(true) or some other instance of vpp is
+                // running(false). This happens when honeycomb is restarted together with vpp using && ,therefore vpp restarts
+                // before honeycomb shutdown starts, and keepalive does not have enough time to trigger therefore vpp
+                // status listener says that vpp is running. This condition prevents honeycomb to invoke disconnect on different vpp
+                // instance than it was connected to, which would ultimately lead to vpp being in state that is unresponsive
+                // to connection attempts.
+                if (isConnectedVppRunning(connectedVppPid)) {
                     registry.close();
                     LOG.info("Successfully disconnected from VPP as {}", config.jvppConnectionName);
-                } catch (Exception e) {
-                    LOG.warn("Unable to properly close jvpp registry", e);
+                } else {
+                    // Handles restart vpp && honeycomb service
+                    LOG.info("VPP instance used for jvpp connection is not alive anymore, no need to disconnect");
                 }
             });
-            LOG.info("JVpp connection opened successfully as: {}", config.jvppConnectionName);
-            return registry;
         } catch (IOException e) {
             throw new IllegalStateException("Unable to open VPP management connection", e);
         }
+        LOG.info("JVpp connection opened successfully as: {}", config.jvppConnectionName);
+        return registry;
+    }
+
+    /**
+     * Tells whether vpp instance that was used for connection is still running
+     */
+    private static boolean isConnectedVppRunning(final long connectedVppPid) {
+        try {
+            final Process process = Runtime.getRuntime().exec(format("ps -eo pid|grep %s", connectedVppPid));
+
+            final BufferedInputStream input = new BufferedInputStream(process.getInputStream());
+            final String processOut = CharStreams.toString(new InputStreamReader(input, Charsets.UTF_8));
+            return processOut.trim().contains(String.valueOf(connectedVppPid));
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Read process id of currently connected vpp
+     */
+    private static long initConnectedVppPid(final JVppRegistry jVppRegistry) {
+        checkState(jVppRegistry.getConnection() instanceof VppJNIConnection, "Connection is not %s", VppJNIConnection.class);
+        final VppJNIConnection jniConnection = VppJNIConnection.class.cast(jVppRegistry.getConnection());
+        final VppJNIConnection.ConnectionInfo jniConnectionInfo = VppJNIConnection.ConnectionInfo.class.cast(
+                jniConnection.getConnectionInfo());
+
+        return UnsignedInts.toLong(jniConnectionInfo.pid);
     }
 }
index b1aab05..2ca7239 100644 (file)
@@ -59,6 +59,7 @@ public class VppCommonModuleTest {
         assertEquals(15, JvppReplyConsumer.JvppReplyTimeoutHolder.getTimeout());
     }
 
+    @Test
     public void testConfigureJVppTimeoutIgnoreOnRetry() {
         initMocks(this);
         Guice.createInjector(new VppCommonModule(), BoundFieldModule.of(this)).injectMembers(this);