Merge "BUG 5746 - Ovsdb QoS and Queue model enhancements"
authorAnil Vishnoi <vishnoianil@gmail.com>
Wed, 22 Jun 2016 00:46:58 +0000 (00:46 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 22 Jun 2016 00:46:58 +0000 (00:46 +0000)
21 files changed:
.gitignore
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepSouthboundUtil.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/PhysicalSwitchUpdateCommand.java
hwvtepsouthbound/hwvtepsouthbound-it/pom.xml
library/features/pom.xml
library/features/src/main/features/features.xml
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionInstance.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbDataTreeChangeListener.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/SouthboundMapper.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/SouthboundUtil.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/ProtocolRemovedCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/ProtocolUpdateCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TerminationPointCreateCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TerminationPointUpdateCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TransactUtils.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/transactions/md/OpenVSwitchUpdateCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/transactions/md/OvsdbBridgeUpdateCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/transactions/md/OvsdbPortUpdateCommand.java
southbound/southbound-impl/src/test/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionInstanceTest.java
utils/ovsdb-it-utils/src/main/java/org/opendaylight/ovsdb/utils/ovsdb/it/utils/DockerOvs.java
utils/ovsdb-it-utils/src/main/java/org/opendaylight/ovsdb/utils/ovsdb/it/utils/ProcUtils.java [new file with mode: 0644]

index 6576f6243cb3b4a5a016470b063553fa9dbe32de..a8dcb6d0a202e05d16c16fd35ac895954509536e 100755 (executable)
@@ -27,6 +27,7 @@ target-ide/
 .vagrant
 .DS_Store
 .checkstyle
+.factorypath
 yang-gen-config
 yang-gen-sal
 maven-metadata-local.xml
index c7e537ad876d2016edacccdf56c29843c2212a50..7da5b5590b0e07b23a3c1f161b2e025686a3ce3f 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalSwitchAttributes;
@@ -30,6 +31,8 @@ import com.google.common.util.concurrent.CheckedFuture;
 public class HwvtepSouthboundUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(HwvtepSouthboundUtil.class);
+    private static final String SCHEMA_VERSION_MISMATCH =
+            "{} column for {} table is not supported by this version of the {} schema: {}";
 
     private static InstanceIdentifierCodec instanceIdentifierCodec;
 
@@ -127,4 +130,9 @@ public class HwvtepSouthboundUtil {
         return String.valueOf(
                 connectionInfo.getRemoteIp().getValue()) + ":" + connectionInfo.getRemotePort().getValue();
     }
+
+
+    public static void schemaMismatchLog(String column, String table, SchemaVersionMismatchException ex) {
+        LOG.debug(SCHEMA_VERSION_MISMATCH, column, table, "hw_vtep", ex.getMessage());
+    }
 }
index f9c20e7272e37b68c42f5b486f31ca65a3776ce0..e7a33b0ba217a14fdb03864755cb89768c113d81 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
 
+import static org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil.schemaMismatchLog;
 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
 
 import java.util.Collection;
@@ -89,7 +90,7 @@ public class PhysicalSwitchUpdateCommand extends AbstractTransactCommand {
             setTunnels(transaction, iid, physicalSwitch, physicalSwitchAugmentation,
                             operationalPhysicalSwitchOptional.isPresent());
         } catch (SchemaVersionMismatchException e) {
-            LOG.debug("tunnels table unsupported for this version of HWVTEP schema", e);
+            schemaMismatchLog("tunnels", "Physical_Switch", e);
         }
         if (!operationalPhysicalSwitchOptional.isPresent()) {
             //create a physical switch
index 1f7ae1a00f222badfb5327768ceec37b4f15fc4b..d914033b4251998d03aa875fefaa2d40c3c59d8e 100644 (file)
@@ -146,6 +146,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-failsafe-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/HwvtepSouthboundIT.java</exclude>
+          </excludes>
+        </configuration>
       </plugin>
       <!-- Needed if you use versionAsInProject() -->
       <plugin>
@@ -169,14 +174,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
           <includeTestSourceDirectory>true</includeTestSourceDirectory>
         </configuration>
       </plugin>
-      <plugin>
-        <artifactId>maven-failsafe-plugin</artifactId>
-        <configuration>
-          <excludes>
-            <exclude>**/HwvtepSouthboundIT.java</exclude>
-          </excludes>
-        </configuration>
-      </plugin>
     </plugins>
   </build>
 
index 94cf39a8ad5b253ee21211c7c1051fa534edb36b..0715b11f5fcc39ff3524d9d1ce892ae311fda42b 100644 (file)
@@ -85,6 +85,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
       <artifactId>schema.openvswitch</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>schema.hardwarevtep</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <!--
index 9713d83461dae6c681ddac785e861910cdb58190..e7a45e868f992340adfae32a0874a9f6b71b8be7 100644 (file)
@@ -20,6 +20,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <!-- Test only (move to another feature) -->
     <bundle>mvn:org.opendaylight.ovsdb/utils.servicehelper/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.ovsdb/schema.openvswitch/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.ovsdb/schema.hardwarevtep/{{VERSION}}</bundle>
     <!-- Test only ends -->
     <bundle start="true">mvn:com.fasterxml.jackson.core/jackson-annotations/{{VERSION}}</bundle>
     <bundle start="true">mvn:com.fasterxml.jackson.core/jackson-core/{{VERSION}}</bundle>
index 69907f2b5b06b8caee9e9eec423cfb27bac05268..ce8181c87202c3885bc0209671e30ef4f6a19e26 100644 (file)
@@ -144,12 +144,9 @@ public class OvsdbConnectionInstance implements OvsdbClient {
         if (transactInvokers == null) {
             try {
                 transactInvokers = new HashMap<>();
-                List<String> databases = getDatabases().get();
-                for (String database : databases) {
-                    DatabaseSchema dbSchema = getSchema(database).get();
-                    if (dbSchema != null) {
-                        transactInvokers.put(dbSchema, new TransactInvokerImpl(this,dbSchema));
-                    }
+                DatabaseSchema dbSchema = getSchema(SouthboundConstants.OPEN_V_SWITCH).get();
+                if(dbSchema != null) {
+                    transactInvokers.put(dbSchema, new TransactInvokerImpl(this,dbSchema));
                 }
             } catch (InterruptedException | ExecutionException e) {
                 LOG.warn("Exception attempting to createTransactionInvokers {}", connectionInfo, e);
index 28a135746ffc55f67d5b32dc3b223bbf9591aec5..b5cebe7b7e5db6ac690e6eb75e659859b37f4db8 100644 (file)
@@ -65,7 +65,6 @@ public class OvsdbDataTreeChangeListener implements ClusteredDataTreeChangeListe
      * @param cm The connection manager.
      */
     OvsdbDataTreeChangeListener(DataBroker db, OvsdbConnectionManager cm) {
-        LOG.info("Registering OvsdbNodeDataChangeListener");
         this.cm = cm;
         this.db = db;
         InstanceIdentifier<Node> path = InstanceIdentifier
@@ -75,11 +74,13 @@ public class OvsdbDataTreeChangeListener implements ClusteredDataTreeChangeListe
         DataTreeIdentifier<Node> dataTreeIdentifier =
                 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, path);
         registration = db.registerDataTreeChangeListener(dataTreeIdentifier, this);
+        LOG.info("OVSDB topology listener has been registered.");
     }
 
     @Override
     public void close() {
         registration.close();
+        LOG.info("OVSDB topology listener has been closed.");
     }
 
     @Override
@@ -119,8 +120,8 @@ public class OvsdbDataTreeChangeListener implements ClusteredDataTreeChangeListe
                     } else {
                         try {
                             InstanceIdentifier<Node> instanceIdentifier = change.getRootPath().getRootIdentifier();
-                            LOG.info("Connecting on key {} to {}", instanceIdentifier, ovsdbNode);
                             cm.connect(instanceIdentifier, ovsdbNode);
+                            LOG.info("OVSDB node has been connected: {}",ovsdbNode);
                         } catch (UnknownHostException e) {
                             LOG.warn("Failed to connect to ovsdbNode", e);
                         }
@@ -140,8 +141,8 @@ public class OvsdbDataTreeChangeListener implements ClusteredDataTreeChangeListe
                     ConnectionInfo key = ovsdbNode.getConnectionInfo();
                     InstanceIdentifier<Node> iid = cm.getInstanceIdentifier(key);
                     try {
-                        LOG.info("Disconnecting from {}", ovsdbNode);
                         cm.disconnect(ovsdbNode);
+                        LOG.info("OVSDB node has been disconnected:{}", ovsdbNode);
                         cm.stopConnectionReconciliationIfActive(iid.firstIdentifierOf(Node.class), ovsdbNode);
                     } catch (UnknownHostException e) {
                         LOG.warn("Failed to disconnect ovsdbNode", e);
index 78512a81623b758e7ae6a7421a6eb7e4a514a7b9..4e7f9bb7d07a0bcecfcd8a03adab6d11b434394f 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.ovsdb.southbound;
 
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
+
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -30,7 +32,6 @@ import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
 import org.opendaylight.ovsdb.schema.openvswitch.Qos;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IetfInetUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
@@ -243,8 +244,7 @@ public class SouthboundMapper {
         try {
             protocols = bridge.getProtocolsColumn().getData();
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.warn("protocols not supported by this version of ovsdb", e);
+            schemaMismatchLog("protocols", "Bridge", e);
         }
         List<ProtocolEntry> protocolList = new ArrayList<>();
         if (protocols != null && protocols.size() > 0) {
index a62820cac51127402c420c992345834f2b3ee41f..2ed79f2fed0e3513be8eae1e4fde3111e931b838 100644 (file)
@@ -20,6 +20,8 @@ import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
+import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAttributes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef;
@@ -33,6 +35,8 @@ import org.slf4j.LoggerFactory;
 public class SouthboundUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(SouthboundUtil.class);
+    private static final String SCHEMA_VERSION_MISMATCH =
+            "{} column for {} table is not supported by this version of the {} schema: {}";
 
     private static InstanceIdentifierCodec instanceIdentifierCodec;
 
@@ -162,4 +166,8 @@ public class SouthboundUtil {
         return String.valueOf(
                 connectionInfo.getRemoteIp().getValue()) + ":" + connectionInfo.getRemotePort().getValue();
     }
+
+    public static void schemaMismatchLog(String column, String table, SchemaVersionMismatchException ex) {
+        LOG.debug(SCHEMA_VERSION_MISMATCH, column, table, SouthboundConstants.OPEN_V_SWITCH, ex.getMessage());
+    }
 }
index cb94560034f618608f174d1878498ea829029c09..5b4203559905997ddcbd0c0ea3966a44fe4fa473 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
 
 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
 
 import java.util.Collection;
 import java.util.Map;
@@ -70,7 +71,7 @@ public class ProtocolRemovedCommand implements TransactCommand {
                             transaction.add(op.mutate(bridge).addMutation(bridge.getProtocolsColumn().getSchema(),
                                     Mutator.DELETE,bridge.getProtocolsColumn().getData()));
                         } catch (SchemaVersionMismatchException e) {
-                            LOG.warn("protocol is not supported by this version of ovsdb", e);
+                            schemaMismatchLog("protocols", "Bridge", e);
                         }
                     }
                 }
index 3eb9f460da1c7615daaa073ca077cd64785e4b9b..1f8ee39d8cc7d81767c99b9d23966d7661def3bb 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
 
 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
 
 import java.util.Collection;
 import java.util.Map;
@@ -83,8 +84,7 @@ public class ProtocolUpdateCommand implements TransactCommand {
                                 .where(bridge.getNameColumn().getSchema().opEqual(bridge.getNameColumn().getData()))
                                 .build());
                         } catch (SchemaVersionMismatchException e) {
-                            // We don't care about the exception stack trace here
-                            LOG.warn("protocol not supported by this version of ovsdb: {}", e.getMessage());
+                            schemaMismatchLog("protocols", "Bridge", e);
                         }
                     }
                 }
index 1bef344ac087c91bd1249210e7a5577614f48a03..56eeed40b053e29d2b2038af35d390f23f95070d 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
 
 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -247,7 +248,7 @@ public class TerminationPointCreateCommand implements TransactCommand {
                 }
             }
         } catch (SchemaVersionMismatchException e) {
-            LOG.debug("lldp column for Interface Table unsupported for this version of ovsdb schema", e);
+            schemaMismatchLog("lldp", "Interface", e);
         }
     }
 
@@ -265,7 +266,7 @@ public class TerminationPointCreateCommand implements TransactCommand {
                 }
             }
         } catch (SchemaVersionMismatchException e) {
-            LOG.debug("bfd column for Interface Table unsupported for this version of ovsdb schema", e);
+            schemaMismatchLog("bfd", "Interface", e);
         }
     }
 
index 0425d39e874eead6b1c9e2c07aee5cb51aeb0f6b..661b8814a666eae0271787664388d4fe388e6d44 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
 
 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
 
 import java.util.Collection;
 import java.util.HashMap;
@@ -268,7 +269,7 @@ public class TerminationPointUpdateCommand implements TransactCommand {
                 }
             }
         } catch (SchemaVersionMismatchException e) {
-            LOG.debug("lldp column for Interface Table unsupported for this version of ovsdb schema", e);
+            schemaMismatchLog("lldp", "Interface", e);
         }
     }
 
@@ -308,7 +309,7 @@ public class TerminationPointUpdateCommand implements TransactCommand {
                 }
             }
         } catch (SchemaVersionMismatchException e) {
-            LOG.debug("bfd column for Interface Table unsupported for this version of ovsdb schema", e);
+            schemaMismatchLog("bfd", "Interface", e);
         }
     }
 
index 3accf490c5fcf8b38ea97d749a19112fee78ef5e..c5e565a2d53add51b093741c64ca96342dd92ef4 100644 (file)
@@ -48,8 +48,6 @@ import org.opendaylight.yangtools.yang.binding.Identifiable;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
@@ -58,8 +56,6 @@ import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
 public class TransactUtils {
-    private static final Logger LOG = LoggerFactory.getLogger(TransactUtils.class);
-
     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
         return new Predicate<DataObjectModification<T>>() {
             @Override
@@ -385,13 +381,9 @@ public class TransactUtils {
         if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) {
             K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey();
             KeyedInstanceIdentifier<N, K> extendedPath = path.child(item, key);
-            LOG.debug("Building a new child iid for {} with {} and key {}, resulting in {}",
-                    path, item, extendedPath);
             return extendedPath;
         } else {
             InstanceIdentifier<N> extendedPath = path.child(item);
-            LOG.debug("Building a new child iid for {} with {}, resulting in {}",
-                    path, item, extendedPath);
             return extendedPath;
         }
     }
index 2ca74ee343b87eef35dc15ad2a7f6a05a30c20ae..7bfb9f0b178ebe53123331b4afd21a93bc725347 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.ovsdb.southbound.transactions.md;
 
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -209,8 +211,7 @@ public class OpenVSwitchUpdateCommand extends AbstractTransactionCommand {
             }
             ovsdbNodeBuilder.setInterfaceTypeEntry(ifEntryList);
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.debug("Iface types  not supported by this version of ovsdb: {}", e.getMessage());
+            schemaMismatchLog("iface_types", SouthboundConstants.OPEN_V_SWITCH, e);
         }
     }
 
@@ -234,8 +235,7 @@ public class OpenVSwitchUpdateCommand extends AbstractTransactionCommand {
             }
             ovsdbNodeBuilder.setDatapathTypeEntry(dpEntryList);
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.debug("Datapath types not supported by this version of ovsdb: {}", e.getMessage());
+            schemaMismatchLog("datapath_types", SouthboundConstants.OPEN_V_SWITCH, e);
         }
     }
 
index 269da04abe1676057f070d5f8a6b5a7582405af8..a86638ffed20e1e8843932631957a854e4b5029d 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.ovsdb.southbound.transactions.md;
 
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
+
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.util.ArrayList;
@@ -185,8 +187,7 @@ public class OvsdbBridgeUpdateCommand extends AbstractTransactionCommand {
                 }
             }
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.warn("protocol not supported by this version of ovsdb: {}", e.getMessage());
+            schemaMismatchLog("protocols", "Bridge", e);
         }
         return result;
     }
@@ -248,7 +249,7 @@ public class OvsdbBridgeUpdateCommand extends AbstractTransactionCommand {
                 }
             }
         } catch (SchemaVersionMismatchException e) {
-            LOG.debug("auto_attach column for Bridge Table unsupported for this version of ovsdb schema. {}", e);
+            schemaMismatchLog("auto_attach", "Bridge", e);
         }
     }
 
index da65b5b13e5bee7b344c564a5beb6b6645df5c55..7a5123dfde1d5afeb57f69dd8508e92ba1c4595a 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.ovsdb.southbound.transactions.md;
 
+import static org.opendaylight.ovsdb.southbound.SouthboundUtil.schemaMismatchLog;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -551,8 +553,7 @@ public class OvsdbPortUpdateCommand extends AbstractTransactionCommand {
                 ovsdbTerminationPointBuilder.setInterfaceLldp(interfaceLldpList);
             }
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.debug("lldp column for Interface Table unsupported for this version of ovsdb schema. {}", e.getMessage());
+            schemaMismatchLog("lldp", "Interface", e);
         }
     }
 
@@ -595,8 +596,7 @@ public class OvsdbPortUpdateCommand extends AbstractTransactionCommand {
                 ovsdbTerminationPointBuilder.setInterfaceBfdStatus(interfaceBfdStatusList);
             }
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.debug("bfd-status column for Interface Table unsupported for this version of ovsdb schema. {}", e.getMessage());
+            schemaMismatchLog("bfd", "Interface", e);
         }
     }
 
@@ -620,8 +620,8 @@ public class OvsdbPortUpdateCommand extends AbstractTransactionCommand {
                 ovsdbTerminationPointBuilder.setInterfaceBfd(interfaceBfdList);
             }
         } catch (SchemaVersionMismatchException e) {
-            // We don't care about the exception stack trace here
-            LOG.debug("bfd column for Interface Table unsupported for this version of ovsdb schema. {}", e.getMessage());
+            schemaMismatchLog("bfd", "Interface", e);
+
         }
     }
 
index d1bc08afcff361361c66fa47e48ff6f4cb9c82dc..2b777022763b62e6b32f42ae92eb8debd5b950ce 100644 (file)
@@ -141,29 +141,21 @@ public class OvsdbConnectionInstanceTest {
         transactInvokers = new HashMap();
         MemberModifier.field(OvsdbConnectionInstance.class, "transactInvokers").set(ovsdbConnectionInstance , transactInvokers);
         ovsdbConnectionInstance.createTransactInvokers();
-        verify(ovsdbConnectionInstance, times(0)).getDatabases();
+        verify(ovsdbConnectionInstance, times(0)).getSchema(anyString());
 
         //transactInvokers null case
         MemberModifier.field(OvsdbConnectionInstance.class, "transactInvokers").set(ovsdbConnectionInstance , null);
-        ListenableFuture<List<String>> listenableFuture = mock(ListenableFuture.class);
-        List<String> databases = new ArrayList<>();
-        databases.add("database1");
-        databases.add("database2");
-        doReturn(listenableFuture).when(ovsdbConnectionInstance).getDatabases();
-        when(listenableFuture.get()).thenReturn(databases);
 
         ListenableFuture<DatabaseSchema> listenableDbSchema = mock(ListenableFuture.class);
         DatabaseSchema dbSchema= mock(DatabaseSchema.class);
-        DatabaseSchema dbSchema1= mock(DatabaseSchema.class);
         doReturn(listenableDbSchema).when(ovsdbConnectionInstance).getSchema(anyString());
-        when(listenableDbSchema.get()).thenReturn(dbSchema).thenReturn(dbSchema1);
+        when(listenableDbSchema.get()).thenReturn(dbSchema);
 
         ovsdbConnectionInstance.createTransactInvokers();
-        verify(ovsdbConnectionInstance).getDatabases();
-        verify(ovsdbConnectionInstance, times(2)).getSchema(anyString());
+        verify(ovsdbConnectionInstance).getSchema(anyString());
 
         Map<DatabaseSchema,TransactInvoker> testTransactInvokers = Whitebox.getInternalState(ovsdbConnectionInstance, "transactInvokers");
-        assertEquals("Error, size of the hashmap is incorrect", 2, testTransactInvokers.size());
+        assertEquals("Error, size of the hashmap is incorrect", 1, testTransactInvokers.size());
     }
 
     @SuppressWarnings("unchecked")
index ad8e6841d74e537655fbcfd664df3b0ab11450ad..1d456de4b15cb06474e56dbc76abee15c1c05ecf 100644 (file)
@@ -8,7 +8,6 @@
 
 package org.opendaylight.ovsdb.utils.ovsdb.it.utils;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
@@ -107,23 +106,28 @@ public class DockerOvs implements AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(DockerOvs.class);
     private static final String DEFAULT_DOCKER_FILE = "docker-ovs-2.5.1.yml";
     private static final String DOCKER_FILE_PATH = "META-INF/docker-compose-files/";
-    //private static final String[] HELP_CMD = {"docker-compose", "--help"};
-    //private static final String[] EXEC_CMD_PFX = {"sudo", "docker-compose", "-f"};
     private static final int COMPOSE_FILE_IDX = 3;
     private static final String DEFAULT_OVSDB_HOST = "127.0.0.1";
     private static final String[] PS_CMD = {"sudo", "docker-compose", "ps"};
     private static final String[] PS_CMD_NO_SUDO = {"docker-compose", "ps"};
 
-    private String[] upCmd = {"sudo", "docker-compose", "-f", null, "up", "-d"};
+    private String[] upCmd = {"sudo", "docker-compose", "-f", null, "up", "-d", "--force-recreate"};
     private String[] downCmd = {"sudo", "docker-compose", "-f", null, "stop"};
+    private String[] execCmd = {"sudo", "docker-compose", "-f", null, "exec", null};
+
     private File tmpDockerComposeFile;
-    private List<String> ovsdbPorts;
     boolean isRunning;
     private String envServerAddress;
     private String envServerPort;
     private String envDockerComposeFile;
     private boolean runDocker;
 
+    class DockerComposeServiceInfo {
+        public String name;
+        public String port;
+    }
+    private List<DockerComposeServiceInfo> dockerComposeServices = new ArrayList<DockerComposeServiceInfo>();
+
     /**
      * Get the array of system properties as pax exam Option objects for use in pax exam
      * unit tests with Configuration annotation.
@@ -166,14 +170,14 @@ public class DockerOvs implements AutoCloseable {
 
         tmpDockerComposeFile = createTempDockerComposeFile(yamlFileName);
         buildDockerComposeCommands();
-        ovsdbPorts = extractPortsFromYaml();
+        parseDockerComposeYaml();
 
         isRunning = false;
         //We run this for A LONG TIME since on the first run docker must download the
         //image from docker hub. In experience it takes significantly less than this
         //even when downloading the image. Once the image is downloaded this command
         //runs like that <snaps fingers>
-        runProcess(60000, upCmd);
+        ProcUtils.runProcess(60000, upCmd);
         isRunning = true;
         waitForOvsdbServers(10 * 1000);
     }
@@ -224,20 +228,24 @@ public class DockerOvs implements AutoCloseable {
     private void buildDockerComposeCommands() throws IOException, InterruptedException {
         upCmd[COMPOSE_FILE_IDX] = tmpDockerComposeFile.toString();
         downCmd[COMPOSE_FILE_IDX] = tmpDockerComposeFile.toString();
+        execCmd[COMPOSE_FILE_IDX] = tmpDockerComposeFile.toString();
 
-        if (0 == tryProcess(5000, PS_CMD_NO_SUDO)) {
+        if (0 == ProcUtils.tryProcess(5000, PS_CMD_NO_SUDO)) {
             LOG.info("DockerOvs.buildDockerComposeCommands docker-compose does not require sudo");
             String[] tmp;
             tmp = Arrays.copyOfRange(upCmd, 1, upCmd.length);
             upCmd = tmp;
             tmp = Arrays.copyOfRange(downCmd, 1, downCmd.length);
             downCmd = tmp;
-        } else if (0 == tryProcess(5000, PS_CMD)) {
+            tmp = Arrays.copyOfRange(execCmd, 1, execCmd.length);
+            execCmd = tmp;
+        } else if (0 == ProcUtils.tryProcess(5000, PS_CMD)) {
             LOG.info("DockerOvs.buildDockerComposeCommands docker-compose requires sudo");
         } else {
             Assert.fail("docker-compose does not seem to work with or without sudo");
         }
     }
+
     /**
      * Get the IP address of the n'th OVS.
      * @param ovsNumber which OVS?
@@ -259,7 +267,7 @@ public class DockerOvs implements AutoCloseable {
         if (!runDocker) {
             return envServerPort;
         }
-        return ovsdbPorts.get(ovsNumber);
+        return dockerComposeServices.get(ovsNumber).port;
     }
 
     /**
@@ -267,14 +275,37 @@ public class DockerOvs implements AutoCloseable {
      * @return number of running OVS nodes
      */
     public int getNumOvsNodes() {
-        return ovsdbPorts.size();
+        return dockerComposeServices.size();
+    }
+
+    public String[] getExecCmdPrefix(int numOvs) {
+        String[] res = new String[execCmd.length];
+        System.arraycopy(execCmd, 0, res, 0, execCmd.length);
+        res[res.length - 1] = dockerComposeServices.get(numOvs).name;
+        return res;
+    }
+
+    public void runInContainer(int waitFor, int numOvs, String ... cmdWords) throws IOException, InterruptedException {
+        String[] pfx = getExecCmdPrefix(numOvs);
+        String[] cmd = new String[pfx.length + cmdWords.length];
+        System.arraycopy(pfx, 0, cmd, 0, pfx.length);
+        System.arraycopy(cmdWords, 0, cmd, pfx.length, cmdWords.length);
+        ProcUtils.runProcess(waitFor, cmd);
+    }
+
+    public void tryInContainer(int waitFor, int numOvs, String ... cmdWords) throws IOException, InterruptedException {
+        String[] pfx = getExecCmdPrefix(numOvs);
+        String[] cmd = new String[pfx.length + cmdWords.length];
+        System.arraycopy(pfx, 0, cmd, 0, pfx.length);
+        System.arraycopy(cmdWords, 0, cmd, pfx.length, cmdWords.length);
+        ProcUtils.tryProcess(waitFor, cmd);
     }
 
     /**
      * Parse the docker-compose yaml file to extract the port mappings.
      * @return a list of the external ports
      */
-    private List<String> extractPortsFromYaml() {
+    private List<String> parseDockerComposeYaml() {
         List<String> ports = new ArrayList<String>();
 
         YamlReader yamlReader = null;
@@ -283,18 +314,24 @@ public class DockerOvs implements AutoCloseable {
             yamlReader = new YamlReader(new FileReader(tmpDockerComposeFile));
             root = (Map) yamlReader.read();
         } catch (FileNotFoundException e) {
-            LOG.warn("DockerOvs.extractPortsFromYaml error reading yaml file", e);
+            LOG.warn("DockerOvs.parseDockerComposeYaml error reading yaml file", e);
             return ports;
         } catch (YamlException e) {
-            LOG.warn("DockerOvs.extractPortsFromYaml error parsing yaml file", e);
+            LOG.warn("DockerOvs.parseDockerComposeYaml error parsing yaml file", e);
             return ports;
         }
 
         if (null == root) {
             return ports;
         }
-        for (Object map : root.values()) {
-            List portMappings = (List) ((Map)map).get("ports");
+        for (Object entry : root.entrySet()) {
+            String key = ((Map.Entry<String,Map>)entry).getKey();
+            Map map = ((Map.Entry<String,Map>)entry).getValue();
+
+            DockerComposeServiceInfo svc = new DockerComposeServiceInfo();
+            svc.name = key;
+
+            List portMappings = (List) map.get("ports");
             if (null == portMappings) {
                 continue;
             }
@@ -306,7 +343,10 @@ public class DockerOvs implements AutoCloseable {
                 }
                 String port = portMappingStr.substring(0, delim);
                 ports.add(port);
+                svc.port = port;
             }
+            //TODO: think this through. What if there is no port?
+            dockerComposeServices.add(svc);
         }
 
         return ports;
@@ -319,7 +359,7 @@ public class DockerOvs implements AutoCloseable {
     @Override
     public void close() throws Exception {
         if (isRunning) {
-            runProcess(5000, downCmd);
+            ProcUtils.runProcess(10000, downCmd);
             isRunning = false;
         }
 
@@ -409,7 +449,7 @@ public class DockerOvs implements AutoCloseable {
     private void waitForOvsdbServers(long waitFor) throws IOException, InterruptedException {
         AtomicInteger numRunningOvs = new AtomicInteger(0);
 
-        int numOvs = ovsdbPorts.size();
+        int numOvs = dockerComposeServices.size();
         if (0 == numOvs) {
             return;
         }
@@ -435,139 +475,6 @@ public class DockerOvs implements AutoCloseable {
         }
     }
 
-    /*
-    WIP - todo: need to extract teh service name from the yaml or receive it as a param
-    private void validateDockerComposeVersion() throws IOException, InterruptedException {
-        StringBuilder stringBuilder = new StringBuilder();
-        runProcess(2000, stringBuilder, HELP_CMD);
-        assertTrue("DockerOvs.validateDockerComposeVersion: docker-compose version does not support exec, try updating",
-                                                                    stringBuilder.toString().contains(" exec "));
-    }
-
-    public String exec(long waitFor, String... execCmdWords) throws IOException, InterruptedException {
-        List<String> execCmd = new ArrayList<String>(20);
-        execCmd.addAll(Arrays.asList(EXEC_CMD_PFX));
-        execCmd.add(tmpDockerComposeFile.toString());
-        execCmd.add("exec");
-        execCmd.add("ovs");
-        execCmd.addAll(Arrays.asList(execCmdWords));
-
-        StringBuilder stringBuilder = new StringBuilder();
-        runProcess(waitFor, stringBuilder, execCmd.toArray(new String[0]));
-        return stringBuilder.toString();
-    }
-    */
-
-    /**
-     * Run a process and assert the exit code is 0.
-     * @param waitFor How long to wait for the command to execute
-     * @param words The words of the command to run
-     * @throws IOException if something goes wrong on the IO end
-     * @throws InterruptedException If this thread is interrupted
-     */
-    private void runProcess(long waitFor, String... words) throws IOException, InterruptedException {
-        runProcess(waitFor, null, words);
-    }
-
-    /**
-     * Run a process, collect the stdout, and assert the exit code is 0.
-     * @param waitFor How long to wait for the command to execute
-     * @param capturedStdout Whatever the process wrote to standard out
-     * @param words The words of the command to run
-     * @throws IOException if something goes wrong on the IO end
-     * @throws InterruptedException If this thread is interrupted
-     */
-    private void runProcess(long waitFor,StringBuilder capturedStdout, String... words)
-                                                                        throws IOException, InterruptedException {
-        int exitValue = tryProcess(waitFor, capturedStdout, words);
-        Assert.assertEquals("DockerOvs.runProcess exit code is not 0", 0, exitValue);
-    }
-
-    /**
-     * Run a process.
-     * @param waitFor How long to wait for the command to execute
-     * @param words The words of the command to run
-     * @return The process's exit code
-     * @throws IOException if something goes wrong on the IO end
-     * @throws InterruptedException If this thread is interrupted
-     */
-    private int tryProcess(long waitFor, String... words) throws IOException, InterruptedException {
-        return tryProcess(waitFor, null, words);
-    }
-
-    /**
-     * Run a process, collect the stdout.
-     * @param waitFor How long to wait (milliseconds) for the command to execute
-     * @param capturedStdout Whatever the process wrote to standard out
-     * @param words The words of the command to run
-     * @return The process's exit code or -1 if the the command does not complete within waitFor milliseconds
-     * @throws IOException if something goes wrong on the IO end
-     * @throws InterruptedException If this thread is interrupted
-     */
-    private int tryProcess(long waitFor, StringBuilder capturedStdout, String... words)
-                                                                        throws IOException, InterruptedException {
-
-        LOG.info("DockerOvs.runProcess running \"{}\", waitFor {}", words, waitFor);
-
-        Process proc = new ProcessBuilder(words).start();
-        int exitValue = -1;
-
-        // Use a try block to guarantee stdout and stderr are closed
-        try (BufferedReader stdout = new BufferedReader(new InputStreamReader(proc.getInputStream()));
-        BufferedReader stderr = new BufferedReader(new InputStreamReader(proc.getErrorStream()))) {
-
-            exitValue = waitForExitValue(waitFor, proc);
-
-            while (stderr.ready()) {
-                LOG.warn("DockerOvs.runProcess [stderr]: {}", stderr.readLine());
-            }
-
-            StringBuilder stdoutStringBuilder = (capturedStdout != null) ? capturedStdout : new StringBuilder();
-            int read;
-            char[] buf = new char[1024];
-            while (-1 != (read = stdout.read(buf))) {
-                stdoutStringBuilder.append(buf, 0, read);
-            }
-
-            for (String line : stdoutStringBuilder.toString().split("\\n")) {
-                LOG.info("DockerOvs.runProcess [stdout]: {}", line);
-            }
-        }
-
-        return exitValue;
-    }
-
-    /**
-     * Wait for a process to end.
-     * @param waitFor how long to wait in milliseconds
-     * @param proc Process object
-     * @return the process's exit value or -1 if the process did not complete within waitFor milliseconds
-     * @throws InterruptedException if this thread is interrupted
-     */
-    private int waitForExitValue(long waitFor, Process proc) throws InterruptedException {
-        //Java 7 has no way to check whether a process is still running without blocking
-        //until the process exits. What this hack does is checks the exitValue() which
-        //throws an IllegalStateException if the process is still running still it does
-        //not have a exit value. We catch that exception and implement our own timeout.
-        //Once we no longer need to support Java 7, this has more elegant solutions.
-        int exitValue = -1;
-        long startTime = System.currentTimeMillis();
-        while (true) {
-            try {
-                exitValue = proc.exitValue();
-                break;
-            } catch (IllegalThreadStateException e) {
-                if ((System.currentTimeMillis() - startTime) < waitFor) {
-                    Thread.sleep(200);
-                } else {
-                    LOG.warn("DockerOvs.waitForExitValue: timed out while waiting for command to complete", e);
-                    break;
-                }
-            }
-        }
-        return exitValue;
-    }
-
     /**
      * Since the docker-compose file is a resource in the bundle and docker-compose needs it.
      * in the file system, we copy it over - ugly but necessary.
@@ -600,4 +507,17 @@ public class DockerOvs implements AutoCloseable {
         return tmpFile;
     }
 
+    /**
+     * Useful for debugging. Dump some interesting config
+     * @throws IOException If something goes wrong with reading the process output
+     * @throws InterruptedException because there's some sleeping in here
+     */
+    public void logState(int dockerInstance) throws IOException, InterruptedException {
+        tryInContainer(5000, dockerInstance, "ip", "addr");
+        tryInContainer(5000, dockerInstance, "ovs-vsctl", "show");
+        tryInContainer(5000, dockerInstance, "ovs-ofctl", "-OOpenFlow13", "show", "br-int");
+        tryInContainer(5000, dockerInstance, "ovs-ofctl", "-OOpenFlow13", "dump-flows", "br-int");
+        tryInContainer(5000, dockerInstance, "ip", "netns", "list");
+    }
+
 }
diff --git a/utils/ovsdb-it-utils/src/main/java/org/opendaylight/ovsdb/utils/ovsdb/it/utils/ProcUtils.java b/utils/ovsdb-it-utils/src/main/java/org/opendaylight/ovsdb/utils/ovsdb/it/utils/ProcUtils.java
new file mode 100644 (file)
index 0000000..73555f3
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.utils.ovsdb.it.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Run subprocesses and log or return their output.
+ */
+public class ProcUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(ProcUtils.class);
+
+     /**
+     * Run a process and assert the exit code is 0.
+     * @param waitFor How long to wait for the command to execute
+     * @param words The words of the command to run
+     * @throws IOException if something goes wrong on the IO end
+     * @throws InterruptedException If this thread is interrupted
+     */
+    public static void runProcess(long waitFor, String... words) throws
+            IOException, InterruptedException {
+        runProcess(waitFor, null, words);
+    }
+
+    /**
+     * Run a process, collect the stdout, and assert the exit code is 0.
+     * @param waitFor How long to wait for the command to execute
+     * @param capturedStdout Whatever the process wrote to standard out
+     * @param words The words of the command to run
+     * @throws IOException if something goes wrong on the IO end
+     * @throws InterruptedException If this thread is interrupted
+     */
+    public static void runProcess(long waitFor,StringBuilder capturedStdout, String... words)
+            throws IOException, InterruptedException {
+        int exitValue = tryProcess(waitFor, capturedStdout, words);
+        Assert.assertEquals("ProcUtils.runProcess exit code is not 0", 0, exitValue);
+    }
+
+    /**
+     * Run a process.
+     * @param waitFor How long to wait for the command to execute
+     * @param words The words of the command to run
+     * @return The process's exit code
+     * @throws IOException if something goes wrong on the IO end
+     * @throws InterruptedException If this thread is interrupted
+     */
+    public static int tryProcess(long waitFor, String... words) throws IOException, InterruptedException {
+        return tryProcess(waitFor, null, words);
+    }
+
+    /**
+     * Run a process, collect the stdout.
+     * @param waitFor How long to wait (milliseconds) for the command to execute
+     * @param capturedStdout Whatever the process wrote to standard out
+     * @param words The words of the command to run
+     * @return The process's exit code or -1 if the the command does not complete within waitFor milliseconds
+     * @throws IOException if something goes wrong on the IO end
+     * @throws InterruptedException If this thread is interrupted
+     */
+    public static int tryProcess(long waitFor, StringBuilder capturedStdout, String... words)
+            throws IOException, InterruptedException {
+
+        LOG.info("ProcUtils.runProcess running \"{}\", waitFor {}", words, waitFor);
+
+        Process proc = new ProcessBuilder(words).start();
+        int exitValue = -1;
+
+        // Use a try block to guarantee stdout and stderr are closed
+        try (BufferedReader stdout = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+             BufferedReader stderr = new BufferedReader(new InputStreamReader(proc.getErrorStream()))) {
+
+            exitValue = waitForExitValue(waitFor, proc);
+
+            while (stderr.ready()) {
+                LOG.warn("ProcUtils.runProcess [stderr]: {}", stderr.readLine());
+            }
+
+            StringBuilder stdoutStringBuilder = (capturedStdout != null) ? capturedStdout : new StringBuilder();
+            int read;
+            char[] buf = new char[1024];
+            while (-1 != (read = stdout.read(buf))) {
+                stdoutStringBuilder.append(buf, 0, read);
+            }
+
+            for (String line : stdoutStringBuilder.toString().split("\\n")) {
+                LOG.info("ProcUtils.runProcess [stdout]: {}", line);
+            }
+        }
+
+        return exitValue;
+    }
+
+    /**
+     * Wait for a process to end.
+     * @param waitFor how long to wait in milliseconds
+     * @param proc Process object
+     * @return the process's exit value or -1 if the process did not complete within waitFor milliseconds
+     * @throws InterruptedException if this thread is interrupted
+     */
+    private static int waitForExitValue(long waitFor, Process proc) throws InterruptedException {
+        //Java 7 has no way to check whether a process is still running without blocking
+        //until the process exits. What this hack does is checks the exitValue() which
+        //throws an IllegalStateException if the process is still running still it does
+        //not have a exit value. We catch that exception and implement our own timeout.
+        //Once we no longer need to support Java 7, this has more elegant solutions.
+        int exitValue = -1;
+        long startTime = System.currentTimeMillis();
+        while (true) {
+            try {
+                exitValue = proc.exitValue();
+                break;
+            } catch (IllegalThreadStateException e) {
+                if ((System.currentTimeMillis() - startTime) < waitFor) {
+                    Thread.sleep(200);
+                } else {
+                    LOG.warn("ProcUtils.waitForExitValue: timed out while waiting for command to complete", e);
+                    break;
+                }
+            }
+        }
+        return exitValue;
+    }
+}