Merge "Make constraint objects serializable by Kryo"
diff --git a/.gitignore b/.gitignore
index 0b28617..8f725d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,5 +8,6 @@
 .checkstyle
 target
 *.iml
+*.pyc
 dependency-reduced-pom.xml
 .idea
diff --git a/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java b/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
index eace91c..5777022 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
@@ -28,4 +28,23 @@
      * Annotation key for latency.
      */
     public static final String LATENCY = "latency";
+
+    /**
+     * Returns the value annotated object for the specified annotation key.
+     * The annotated value is expected to be String that can be parsed as double.
+     * If parsing fails, the returned value will be 1.0.
+     *
+     * @param annotated annotated object whose annotated value is obtained
+     * @param key key of annotation
+     * @return double value of annotated object for the specified key
+     */
+    public static double getAnnotatedValue(Annotated annotated, String key) {
+        double value;
+        try {
+            value = Double.parseDouble(annotated.annotations().value(key));
+        } catch (NumberFormatException e) {
+            value = 1.0;
+        }
+        return value;
+    }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
index c2d901b..c6cb361 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
@@ -80,6 +80,7 @@
         this.state = FlowEntryState.FAILED;
         this.errType = errType;
         this.errCode = errCode;
+        this.lastSeen = System.currentTimeMillis();
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
index 413473f..673773a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
@@ -63,7 +63,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(criteria);
+        return criteria.hashCode();
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
index de2c7fd..64a56ca 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
@@ -18,7 +18,7 @@
 import org.onlab.onos.core.ApplicationId;
 import org.onlab.onos.net.provider.Provider;
 
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.Future;
 
 /**
  * Abstraction of a flow rule provider.
@@ -58,6 +58,6 @@
      * @param batch a batch of flow rules
      * @return a future indicating the status of this execution
      */
-    ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
+    Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
index bac1bab..61fe54d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
@@ -196,7 +196,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(port, type());
+            return Objects.hash(type(), port);
         }
 
         @Override
@@ -242,7 +242,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mac, type);
+            return Objects.hash(type, mac);
         }
 
         @Override
@@ -288,7 +288,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(ethType, type());
+            return Objects.hash(type(), ethType);
         }
 
         @Override
@@ -336,7 +336,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(ip, type);
+            return Objects.hash(type, ip);
         }
 
         @Override
@@ -382,7 +382,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(proto, type());
+            return Objects.hash(type(), proto);
         }
 
         @Override
@@ -427,7 +427,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(vlanPcp);
+            return Objects.hash(type(), vlanPcp);
         }
 
         @Override
@@ -474,7 +474,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(vlanId, type());
+            return Objects.hash(type(), vlanId);
         }
 
         @Override
@@ -522,7 +522,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(tcpPort, type);
+            return Objects.hash(type, tcpPort);
         }
 
         @Override
@@ -568,7 +568,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(lambda, type);
+            return Objects.hash(type, lambda);
         }
 
         @Override
@@ -612,7 +612,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(signalType, type);
+            return Objects.hash(type, signalType);
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
index 0e77f4a..7dc0f8d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
@@ -190,7 +190,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(port, type());
+            return Objects.hash(type(), port);
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java
index 0d5cd81..25fe79f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java
@@ -70,7 +70,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(lambda, type(), subtype);
+            return Objects.hash(type(), subtype, lambda);
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
index abe19e3..20eaf6e 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
@@ -93,7 +93,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mac, type(), subtype);
+            return Objects.hash(type(), subtype, mac);
         }
 
         @Override
@@ -142,7 +142,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(vlanId, type(), subtype());
+            return Objects.hash(type(), subtype(), vlanId);
         }
 
         @Override
@@ -191,7 +191,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(vlanPcp, type(), subtype());
+            return Objects.hash(type(), subtype(), vlanPcp);
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
index 89a8cda..e8b72e7 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
@@ -85,7 +85,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(ip, type(), subtype());
+            return Objects.hash(type(), subtype(), ip);
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java
index 59b5ad2..1767a41 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java
@@ -21,6 +21,8 @@
 
 import java.util.Objects;
 
+import static org.onlab.onos.net.AnnotationKeys.getAnnotatedValue;
+
 /**
  * Constraint that evaluates an arbitrary link annotated value is under the specified threshold.
  */
@@ -71,25 +73,6 @@
         return value <= threshold;
     }
 
-    /**
-     * Returns the annotated value of the specified link. The annotated value
-     * is expected to be String that can be parsed as double. If parsing fails,
-     * the returned value will be 1.0.
-     *
-     * @param link link whose annotated value is obtained
-     * @param key key of link annotation
-     * @return double value of link annotation for the specified key
-     */
-    private double getAnnotatedValue(Link link, String key) {
-        double value;
-        try {
-            value = Double.parseDouble(link.annotations().value(key));
-        } catch (NumberFormatException e) {
-            value = 1.0;
-        }
-        return value;
-    }
-
     @Override
     public double cost(Link link, LinkResourceService resourceService) {
         if (isValid(link, resourceService)) {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
index a2a25b6..e4b4432 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
@@ -26,6 +26,7 @@
 import java.util.Objects;
 
 import static org.onlab.onos.net.AnnotationKeys.LATENCY;
+import static org.onlab.onos.net.AnnotationKeys.getAnnotatedValue;
 
 /**
  * Constraint that evaluates the latency through a path.
@@ -53,16 +54,7 @@
 
     @Override
     public double cost(Link link, LinkResourceService resourceService) {
-        String value = link.annotations().value(LATENCY);
-
-        double latencyInMicroSec;
-        try {
-            latencyInMicroSec = Double.parseDouble(value);
-        } catch (NumberFormatException e) {
-            latencyInMicroSec = 1.0;
-        }
-
-        return latencyInMicroSec;
+        return getAnnotatedValue(link, LATENCY);
     }
 
     @Override
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index e996dfc..7e5f049 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -15,22 +15,12 @@
  */
 package org.onlab.onos.net.flow.impl;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.onlab.util.Tools.namedThreads;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
-
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -64,14 +54,21 @@
 import org.onlab.onos.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Provides implementation of the flow NB &amp; SB APIs.
@@ -92,8 +89,7 @@
 
     private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
 
-    private final ExecutorService futureListeners =
-            Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
+    private ExecutorService futureService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowRuleStore store;
@@ -106,6 +102,7 @@
 
     @Activate
     public void activate() {
+        futureService = Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
         store.setDelegate(delegate);
         eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry);
         log.info("Started");
@@ -113,7 +110,7 @@
 
     @Deactivate
     public void deactivate() {
-        futureListeners.shutdownNow();
+        futureService.shutdownNow();
 
         store.unsetDelegate(delegate);
         eventDispatcher.removeSink(FlowRuleEvent.class);
@@ -364,6 +361,9 @@
 
     // Store delegate to re-post events emitted from the store.
     private class InternalStoreDelegate implements FlowRuleStoreDelegate {
+
+        private static final int TIMEOUT = 5000; // ms
+
         // TODO: Right now we only dispatch events at individual flowEntry level.
         // It may be more efficient for also dispatch events as a batch.
         @Override
@@ -384,15 +384,21 @@
 
                 FlowRuleProvider flowRuleProvider =
                         getProvider(batchOperation.getOperations().get(0).getTarget().deviceId());
-                final ListenableFuture<CompletedBatchOperation> result =
+                final Future<CompletedBatchOperation> result =
                         flowRuleProvider.executeBatch(batchOperation);
-                result.addListener(new Runnable() {
+                futureService.submit(new Runnable() {
                     @Override
                     public void run() {
-                        store.batchOperationComplete(FlowRuleBatchEvent.completed(request,
-                                                                                  Futures.getUnchecked(result)));
+                        CompletedBatchOperation res = null;
+                        try {
+                            res = result.get(TIMEOUT, TimeUnit.MILLISECONDS);
+                        } catch (TimeoutException | InterruptedException | ExecutionException e) {
+                            log.warn("Something went wrong with the batch operation {}",
+                                     request.batchId());
+                        }
+                        store.batchOperationComplete(FlowRuleBatchEvent.completed(request, res));
                     }
-                }, futureListeners);
+                });
                 break;
 
             case BATCH_OPERATION_COMPLETED:
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
new file mode 100644
index 0000000..b8d7799
--- /dev/null
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.flow;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.onlab.onos.net.intent.IntentTestsMocks;
+
+import com.google.common.testing.EqualsTester;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.onos.net.NetTestTools.did;
+
+/**
+ * Unit tests for the DefaultFlowEntry class.
+ */
+public class DefaultFlowEntryTest {
+    private static final IntentTestsMocks.MockSelector SELECTOR =
+            new IntentTestsMocks.MockSelector();
+    private static final IntentTestsMocks.MockTreatment TREATMENT =
+            new IntentTestsMocks.MockTreatment();
+
+    private static DefaultFlowEntry makeFlowEntry(int uniqueValue) {
+        return new DefaultFlowEntry(did("id" + Integer.toString(uniqueValue)),
+                SELECTOR,
+                TREATMENT,
+                uniqueValue,
+                FlowEntry.FlowEntryState.ADDED,
+                uniqueValue,
+                uniqueValue,
+                uniqueValue,
+                uniqueValue,
+                uniqueValue);
+    }
+
+    final DefaultFlowEntry defaultFlowEntry1 = makeFlowEntry(1);
+    final DefaultFlowEntry sameAsDefaultFlowEntry1 = makeFlowEntry(1);
+    final DefaultFlowEntry defaultFlowEntry2 = makeFlowEntry(2);
+
+    /**
+     * Tests the equals, hashCode and toString methods using Guava EqualsTester.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(defaultFlowEntry1, sameAsDefaultFlowEntry1)
+                .addEqualityGroup(defaultFlowEntry2)
+                .testEquals();
+    }
+
+    /**
+     * Tests the construction of a default flow entry from a device id.
+     */
+    @Test
+    public void testDeviceBasedObject() {
+        assertThat(defaultFlowEntry1.deviceId(), is(did("id1")));
+        assertThat(defaultFlowEntry1.selector(), is(SELECTOR));
+        assertThat(defaultFlowEntry1.treatment(), is(TREATMENT));
+        assertThat(defaultFlowEntry1.timeout(), is(1));
+        assertThat(defaultFlowEntry1.life(), is(1L));
+        assertThat(defaultFlowEntry1.packets(), is(1L));
+        assertThat(defaultFlowEntry1.bytes(), is(1L));
+        assertThat(defaultFlowEntry1.state(), is(FlowEntry.FlowEntryState.ADDED));
+        assertThat(defaultFlowEntry1.lastSeen(),
+                   greaterThan(System.currentTimeMillis() -
+                           TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+    }
+
+    /**
+     * Tests the setters on a default flow entry object.
+     */
+    @Test
+    public void testSetters() {
+        final DefaultFlowEntry entry = makeFlowEntry(1);
+
+        entry.setLastSeen();
+        entry.setState(FlowEntry.FlowEntryState.PENDING_REMOVE);
+        entry.setPackets(11);
+        entry.setBytes(22);
+        entry.setLife(33);
+
+        assertThat(entry.deviceId(), is(did("id1")));
+        assertThat(entry.selector(), is(SELECTOR));
+        assertThat(entry.treatment(), is(TREATMENT));
+        assertThat(entry.timeout(), is(1));
+        assertThat(entry.life(), is(33L));
+        assertThat(entry.packets(), is(11L));
+        assertThat(entry.bytes(), is(22L));
+        assertThat(entry.state(), is(FlowEntry.FlowEntryState.PENDING_REMOVE));
+        assertThat(entry.lastSeen(),
+                greaterThan(System.currentTimeMillis() -
+                        TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+    }
+
+    /**
+     * Tests a default flow rule built for an error.
+     */
+    @Test
+    public void testErrorObject() {
+        final DefaultFlowEntry errorEntry =
+                new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(1),
+                                     111,
+                                     222);
+        assertThat(errorEntry.errType(), is(111));
+        assertThat(errorEntry.errCode(), is(222));
+        assertThat(errorEntry.state(), is(FlowEntry.FlowEntryState.FAILED));
+        assertThat(errorEntry.lastSeen(),
+                greaterThan(System.currentTimeMillis() -
+                        TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+    }
+
+    /**
+     * Tests a default flow entry constructed from a flow rule.
+     */
+    @Test
+    public void testFlowBasedObject() {
+        final DefaultFlowEntry entry =
+                new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(1));
+        assertThat(entry.priority(), is(1));
+        assertThat(entry.appId(), is((short) 0));
+        assertThat(entry.lastSeen(),
+                greaterThan(System.currentTimeMillis() -
+                        TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+    }
+
+    /**
+     * Tests a default flow entry constructed from a flow rule plus extra
+     * parameters.
+     */
+    @Test
+    public void testFlowBasedObjectWithParameters() {
+        final DefaultFlowEntry entry =
+                new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(33),
+                        FlowEntry.FlowEntryState.REMOVED,
+                        101, 102, 103);
+        assertThat(entry.state(), is(FlowEntry.FlowEntryState.REMOVED));
+        assertThat(entry.life(), is(101L));
+        assertThat(entry.packets(), is(102L));
+        assertThat(entry.bytes(), is(103L));
+        assertThat(entry.lastSeen(),
+                greaterThan(System.currentTimeMillis() -
+                        TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+    }
+}
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
index cddaf23..f2c418f 100644
--- a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
@@ -17,7 +17,6 @@
 package org.onlab.onos.net.flow;
 
 import org.junit.Test;
-import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.intent.IntentTestsMocks;
 
 import com.google.common.testing.EqualsTester;
@@ -25,8 +24,8 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBaseClass;
-import static org.onlab.onos.net.NetTestTools.did;
 import static org.onlab.onos.net.NetTestTools.APP_ID;
+import static org.onlab.onos.net.NetTestTools.did;
 
 /**
  * Unit tests for the default flow rule class.
@@ -37,63 +36,13 @@
     private static final IntentTestsMocks.MockTreatment TREATMENT =
             new IntentTestsMocks.MockTreatment();
 
-    final FlowRule flowRule1 = new MockFlowRule(1);
-    final FlowRule sameAsFlowRule1 = new MockFlowRule(1);
-    final FlowRule flowRule2 = new MockFlowRule(2);
+    final FlowRule flowRule1 = new IntentTestsMocks.MockFlowRule(1);
+    final FlowRule sameAsFlowRule1 = new IntentTestsMocks.MockFlowRule(1);
+    final FlowRule flowRule2 = new IntentTestsMocks.MockFlowRule(2);
     final DefaultFlowRule defaultFlowRule1 = new DefaultFlowRule(flowRule1);
     final DefaultFlowRule sameAsDefaultFlowRule1 = new DefaultFlowRule(sameAsFlowRule1);
     final DefaultFlowRule defaultFlowRule2 = new DefaultFlowRule(flowRule2);
 
-    private static class MockFlowRule implements FlowRule {
-
-        int priority;
-        MockFlowRule(int priority) {
-            this.priority = priority;
-        }
-
-        @Override
-        public FlowId id() {
-            return FlowId.valueOf(1);
-        }
-
-        @Override
-        public short appId() {
-            return 0;
-        }
-
-        @Override
-        public int priority() {
-            return priority;
-        }
-
-        @Override
-        public DeviceId deviceId() {
-            return did("1");
-        }
-
-        @Override
-        public TrafficSelector selector() {
-            return SELECTOR;
-        }
-
-        @Override
-        public TrafficTreatment treatment() {
-            return TREATMENT;
-        }
-
-        @Override
-        public int timeout() {
-            return 0;
-        }
-
-        @Override
-        public boolean isPermanent() {
-            return false;
-        }
-
-
-    }
-
     /**
      * Checks that the DefaultFlowRule class is immutable but can be inherited
      * from.
diff --git a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java b/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
index e0fe09e..65df6b2 100644
--- a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
+++ b/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
@@ -16,6 +16,7 @@
 package org.onlab.onos.net.intent;
 
 import static org.onlab.onos.net.NetTestTools.createPath;
+import static org.onlab.onos.net.NetTestTools.did;
 import static org.onlab.onos.net.NetTestTools.link;
 
 import java.util.ArrayList;
@@ -31,6 +32,8 @@
 import org.onlab.onos.net.ElementId;
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.Path;
+import org.onlab.onos.net.flow.FlowId;
+import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 import org.onlab.onos.net.flow.criteria.Criterion;
@@ -271,4 +274,60 @@
         }
     }
 
+    private static final IntentTestsMocks.MockSelector SELECTOR =
+            new IntentTestsMocks.MockSelector();
+    private static final IntentTestsMocks.MockTreatment TREATMENT =
+            new IntentTestsMocks.MockTreatment();
+
+    public static class MockFlowRule implements FlowRule {
+
+        int priority;
+        public MockFlowRule(int priority) {
+            this.priority = priority;
+        }
+
+        @Override
+        public FlowId id() {
+            return FlowId.valueOf(1);
+        }
+
+        @Override
+        public short appId() {
+            return 0;
+        }
+
+        @Override
+        public int priority() {
+            return priority;
+        }
+
+        @Override
+        public DeviceId deviceId() {
+            return did("1");
+        }
+
+        @Override
+        public TrafficSelector selector() {
+            return SELECTOR;
+        }
+
+        @Override
+        public TrafficTreatment treatment() {
+            return TREATMENT;
+        }
+
+        @Override
+        public int timeout() {
+            return 0;
+        }
+
+        @Override
+        public boolean isPermanent() {
+            return false;
+        }
+
+
+    }
+
+
 }
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
index 018d6f3..214bde3 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -15,21 +15,11 @@
  */
 package org.onlab.onos.provider.of.flow.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ExecutionList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -80,15 +70,23 @@
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
 import org.projectfloodlight.openflow.types.OFPort;
-import org.projectfloodlight.openflow.types.U32;
 import org.slf4j.Logger;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Provider which uses an OpenFlow controller to detect network
@@ -124,6 +122,8 @@
 
     private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
 
+    private final AtomicLong xidCounter = new AtomicLong(0);
+
     /**
      * Creates an OpenFlow host provider.
      */
@@ -154,6 +154,7 @@
 
         log.info("Stopped");
     }
+
     @Override
     public void applyFlowRule(FlowRule... flowRules) {
         for (int i = 0; i < flowRules.length; i++) {
@@ -167,7 +168,6 @@
     }
 
 
-
     @Override
     public void removeFlowRule(FlowRule... flowRules) {
         for (int i = 0; i < flowRules.length; i++) {
@@ -188,11 +188,15 @@
     }
 
     @Override
-    public ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
+    public Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
         final Set<Dpid> sws =
                 Collections.newSetFromMap(new ConcurrentHashMap<Dpid, Boolean>());
         final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<Long, FlowRuleBatchEntry>();
-        OFFlowMod mod = null;
+        /*
+         * Use identity hash map for reference equality as we could have equal
+         * flow mods for different switches.
+         */
+        Map<OFFlowMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
         for (FlowRuleBatchEntry fbe : batch.getOperations()) {
             FlowRule flowRule = fbe.getTarget();
             OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
@@ -208,6 +212,7 @@
             }
             sws.add(new Dpid(sw.getId()));
             FlowModBuilder builder = FlowModBuilder.builder(flowRule, sw.factory());
+            OFFlowMod mod = null;
             switch (fbe.getOperator()) {
                 case ADD:
                     mod = builder.buildFlowAdd();
@@ -222,25 +227,29 @@
                     log.error("Unsupported batch operation {}", fbe.getOperator());
             }
             if (mod != null) {
-                sw.sendMsg(mod);
-                fmXids.put(mod.getXid(), fbe);
+                mods.put(mod, sw);
+                fmXids.put(xidCounter.getAndIncrement(), fbe);
             } else {
                 log.error("Conversion of flowrule {} failed.", flowRule);
             }
-
         }
         InstallationFuture installation = new InstallationFuture(sws, fmXids);
         for (Long xid : fmXids.keySet()) {
             pendingFMs.put(xid, installation);
         }
-        pendingFutures.put(U32.f(batch.hashCode()), installation);
-        installation.verify(U32.f(batch.hashCode()));
+        pendingFutures.put(installation.xid(), installation);
+        for (Map.Entry<OFFlowMod, OpenFlowSwitch> entry : mods.entrySet()) {
+            OpenFlowSwitch sw = entry.getValue();
+            OFFlowMod mod = entry.getKey();
+            sw.sendMsg(mod);
+        }
+        installation.verify();
         return installation;
     }
 
 
     private class InternalFlowProvider
-    implements OpenFlowSwitchListener, OpenFlowEventListener {
+            implements OpenFlowSwitchListener, OpenFlowEventListener {
 
 
         private final Multimap<DeviceId, FlowEntry> completeEntries =
@@ -274,36 +283,36 @@
         public void handleMessage(Dpid dpid, OFMessage msg) {
             InstallationFuture future = null;
             switch (msg.getType()) {
-            case FLOW_REMOVED:
-                OFFlowRemoved removed = (OFFlowRemoved) msg;
+                case FLOW_REMOVED:
+                    OFFlowRemoved removed = (OFFlowRemoved) msg;
 
-                FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
-                providerService.flowRemoved(fr);
-                break;
-            case STATS_REPLY:
-                pushFlowMetrics(dpid, (OFStatsReply) msg);
-                break;
-            case BARRIER_REPLY:
-                future = pendingFutures.get(msg.getXid());
-                if (future != null) {
-                    future.satisfyRequirement(dpid);
-                }
-                break;
-            case ERROR:
-                future = pendingFMs.get(msg.getXid());
-                if (future != null) {
-                    future.fail((OFErrorMsg) msg, dpid);
-                }
-                break;
-            default:
-                log.debug("Unhandled message type: {}", msg.getType());
+                    FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
+                    providerService.flowRemoved(fr);
+                    break;
+                case STATS_REPLY:
+                    pushFlowMetrics(dpid, (OFStatsReply) msg);
+                    break;
+                case BARRIER_REPLY:
+                    future = pendingFutures.get(msg.getXid());
+                    if (future != null) {
+                        future.satisfyRequirement(dpid);
+                    }
+                    break;
+                case ERROR:
+                    future = pendingFMs.get(msg.getXid());
+                    if (future != null) {
+                        future.fail((OFErrorMsg) msg, dpid);
+                    }
+                    break;
+                default:
+                    log.debug("Unhandled message type: {}", msg.getType());
             }
 
         }
 
         @Override
         public void receivedRoleReply(Dpid dpid, RoleState requested,
-                RoleState response) {
+                                      RoleState response) {
             // Do nothing here for now.
         }
 
@@ -352,8 +361,9 @@
 
     }
 
-    private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> {
+    private class InstallationFuture implements Future<CompletedBatchOperation> {
 
+        private final Long xid;
         private final Set<Dpid> sws;
         private final AtomicBoolean ok = new AtomicBoolean(true);
         private final Map<Long, FlowRuleBatchEntry> fms;
@@ -361,18 +371,22 @@
         private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet();
 
         private final CountDownLatch countDownLatch;
-        private Long pendingXid;
         private BatchState state;
 
         private final ExecutionList executionList = new ExecutionList();
 
         public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) {
+            this.xid = xidCounter.getAndIncrement();
             this.state = BatchState.STARTED;
             this.sws = sws;
             this.fms = fmXids;
             countDownLatch = new CountDownLatch(sws.size());
         }
 
+        public Long xid() {
+            return xid;
+        }
+
         public void fail(OFErrorMsg msg, Dpid dpid) {
 
             ok.set(false);
@@ -385,27 +399,27 @@
                 case BAD_ACTION:
                     OFBadActionErrorMsg bad = (OFBadActionErrorMsg) msg;
                     fe = new DefaultFlowEntry(offending, bad.getErrType().ordinal(),
-                            bad.getCode().ordinal());
+                                              bad.getCode().ordinal());
                     break;
                 case BAD_INSTRUCTION:
                     OFBadInstructionErrorMsg badins = (OFBadInstructionErrorMsg) msg;
                     fe = new DefaultFlowEntry(offending, badins.getErrType().ordinal(),
-                            badins.getCode().ordinal());
+                                              badins.getCode().ordinal());
                     break;
                 case BAD_MATCH:
                     OFBadMatchErrorMsg badMatch = (OFBadMatchErrorMsg) msg;
                     fe = new DefaultFlowEntry(offending, badMatch.getErrType().ordinal(),
-                            badMatch.getCode().ordinal());
+                                              badMatch.getCode().ordinal());
                     break;
                 case BAD_REQUEST:
                     OFBadRequestErrorMsg badReq = (OFBadRequestErrorMsg) msg;
                     fe = new DefaultFlowEntry(offending, badReq.getErrType().ordinal(),
-                            badReq.getCode().ordinal());
+                                              badReq.getCode().ordinal());
                     break;
                 case FLOW_MOD_FAILED:
                     OFFlowModFailedErrorMsg fmFail = (OFFlowModFailedErrorMsg) msg;
                     fe = new DefaultFlowEntry(offending, fmFail.getErrType().ordinal(),
-                            fmFail.getCode().ordinal());
+                                              fmFail.getCode().ordinal());
                     break;
                 case EXPERIMENTER:
                 case GROUP_MOD_FAILED:
@@ -434,13 +448,12 @@
         }
 
 
-        public void verify(Long id) {
-            pendingXid = id;
+        public void verify() {
             for (Dpid dpid : sws) {
                 OpenFlowSwitch sw = controller.getSwitch(dpid);
                 OFBarrierRequest.Builder builder = sw.factory()
                         .buildBarrierRequest()
-                        .setXid(id);
+                        .setXid(xid);
                 sw.sendMsg(builder.build());
             }
         }
@@ -462,7 +475,6 @@
                 }
 
             }
-            invokeCallbacks();
             return true;
         }
 
@@ -481,6 +493,7 @@
             countDownLatch.await();
             this.state = BatchState.FINISHED;
             CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
+            //FIXME do cleanup here
             return result;
         }
 
@@ -491,6 +504,7 @@
             if (countDownLatch.await(timeout, unit)) {
                 this.state = BatchState.FINISHED;
                 CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
+                // FIXME do cleanup here
                 return result;
             }
             throw new TimeoutException();
@@ -498,9 +512,7 @@
 
         private void cleanUp() {
             if (isDone() || isCancelled()) {
-                if (pendingXid != null) {
-                    pendingFutures.remove(pendingXid);
-                }
+                pendingFutures.remove(xid);
                 for (Long xid : fms.keySet()) {
                     pendingFMs.remove(xid);
                 }
@@ -509,21 +521,10 @@
 
         private void removeRequirement(Dpid dpid) {
             countDownLatch.countDown();
-            if (countDownLatch.getCount() == 0) {
-                invokeCallbacks();
-            }
             sws.remove(dpid);
+            //FIXME don't do cleanup here
             cleanUp();
         }
-
-        @Override
-        public void addListener(Runnable runnable, Executor executor) {
-            executionList.add(runnable, executor);
-        }
-
-        private void invokeCallbacks() {
-            executionList.execute();
-        }
     }
 
 }
diff --git a/tools/test/topos/oe-nonlinear-10.json b/tools/test/topos/oe-nonlinear-10.json
index f23bb9b..522215b 100644
--- a/tools/test/topos/oe-nonlinear-10.json
+++ b/tools/test/topos/oe-nonlinear-10.json
@@ -108,33 +108,26 @@
     ],
 
     "links" : [
-        { "src": "of:0000ffffffffff01/10", "dst": "of:0000ffffffffff02/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff02/10", "dst": "of:0000ffffffffff03/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff03/30", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff05/20", "dst": "of:0000ffffffffff07/21", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff08/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
-        { "src": "of:0000ffffffffff08/30", "dst": "of:0000ffffffffff0A/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff01/50", "dst": "of:0000ffffffffff02/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff02/50", "dst": "of:0000ffffffffff03/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff03/50", "dst": "of:0000ffffffffff04/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff05/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff04/20", "dst": "of:0000ffffffffff06/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/40","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff05/40", "dst": "of:0000ffffffffff07/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff08/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff09/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+        { "src": "of:0000ffffffffff08/20", "dst": "of:0000ffffffffff0A/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
         { "src": "of:0000ffffffffff09/20", "dst": "of:0000ffffffffff0A/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
 
-        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff03/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff04/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0007/2", "dst": "of:0000ffffffffff07/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff0009/2", "dst": "of:0000ffffffffff09/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
-        { "src": "of:0000ffffffff000A/2", "dst": "of:0000ffffffffff0A/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
-    ],
-
-    "hosts" : [
-        { "mac": "00:00:00:00:00:01", "vlan": -1, "location": "of:0000ffffffff0001/1", "ip": "10.0.0.1" },
-        { "mac": "00:00:00:00:00:03", "vlan": -1, "location": "of:0000ffffffff0003/1", "ip": "10.0.0.3" },
-        { "mac": "00:00:00:00:00:04", "vlan": -1, "location": "of:0000ffffffff0004/1", "ip": "10.0.0.4" },
-        { "mac": "00:00:00:00:00:07", "vlan": -1, "location": "of:0000ffffffff0007/1", "ip": "10.0.0.7" },
-        { "mac": "00:00:00:00:00:09", "vlan": -1, "location": "of:0000ffffffff0009/1", "ip": "10.0.0.9" },
-        { "mac": "00:00:00:00:00:0A", "vlan": -1, "location": "of:0000ffffffff000A/1", "ip": "10.0.0.10" }
+        { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+        { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+        { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+        { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff07/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+        { "src": "of:0000ffffffff0005/2", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+        { "src": "of:0000ffffffff0006/2", "dst": "of:0000ffffffffff0A/10",  "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
     ]
-}
\ No newline at end of file
+}