building flowrules
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
index df7a408..6856e1a 100644
--- a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
+++ b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
@@ -169,7 +169,7 @@
         treat.add(Instructions.createOutput(portNumber));
 
         FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
-                                         builder.build(), treat.build());
+                                         builder.build(), treat.build(), 0);
 
         flowRuleService.applyFlowRules(f);
     }
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
deleted file mode 100644
index 295517b..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package org.onlab.onos.net.flow;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-import org.onlab.onos.net.DeviceId;
-
-public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
-
-    private final int priority;
-    private final long created;
-    private final FlowId id;
-
-    public DefaultFlowEntry(DefaultFlowEntry entry) {
-        super(entry.deviceId(), entry.selector(), entry.treatment());
-        this.priority = entry.priority;
-        this.created = entry.created;
-        this.id = entry.id;
-    }
-
-    public DefaultFlowEntry(DeviceId deviceId, TrafficSelector selector,
-            TrafficTreatment treatment, int priority) {
-        super(deviceId, selector, treatment);
-        this.priority = priority;
-        this.created = System.currentTimeMillis();
-        this.id = FlowId.valueOf(this.hashCode());
-    }
-
-    @Override
-    public FlowId id() {
-        return null;
-    }
-
-    @Override
-    public int priority() {
-        return priority;
-    }
-
-    @Override
-    public long lifeMillis() {
-        return (created - System.currentTimeMillis());
-    }
-
-    @Override
-    public long idleMillis() {
-        return 0;
-    }
-
-    @Override
-    public long packets() {
-        return 0;
-    }
-
-    @Override
-    public long bytes() {
-        return 0;
-    }
-
-    @Override
-    /*
-     * currently uses the parts that definitely have a defined hashcode...
-     *
-     * (non-Javadoc)
-     * @see java.lang.Object#hashCode()
-     */
-    public int hashCode() {
-        final int prime = 31;
-        int result = prime * this.deviceId().hashCode();
-        result = prime * result + this.priority;
-        result = prime * result + this.selector().hashCode();
-        result = prime * result + this.treatment().hashCode();
-        return result;
-    }
-
-    @Override
-    /*
-     * The priority and statistics can change on a given treatment and selector
-     *
-     * (non-Javadoc)
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    public boolean equals(Object obj) {
-        if (obj instanceof DefaultFlowEntry) {
-            DefaultFlowEntry that = (DefaultFlowEntry) obj;
-            if (!this.deviceId().equals(that.deviceId())) {
-                return false;
-            }
-            if (!(this.priority == that.priority)) {
-                return false;
-            }
-            return super.equals(obj);
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(this)
-                .add("id", id)
-                .add("deviceId", deviceId())
-                .add("priority", priority)
-                .add("selector", selector())
-                .add("treatment", treatment())
-                .toString();
-    }
-
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index d20e79e..564416c 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -1,24 +1,57 @@
 package org.onlab.onos.net.flow;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 import org.onlab.onos.net.DeviceId;
 
 public class DefaultFlowRule implements FlowRule {
 
+    private final DeviceId deviceId;
+    private final int priority;
     private final TrafficSelector selector;
     private final TrafficTreatment treatment;
-    private final DeviceId deviceId;
+    private final FlowId id;
+    private final long created;
+    private long life;
+    private long idle;
+    private long packets;
+    private long bytes;
+
 
     public DefaultFlowRule(DeviceId deviceId,
-            TrafficSelector selector, TrafficTreatment treatment) {
-        this.treatment = treatment;
-        this.selector = selector;
+            TrafficSelector selector, TrafficTreatment treatment, int priority) {
         this.deviceId = deviceId;
+        this.priority = priority;
+        this.selector = selector;
+        this.treatment = treatment;
+        this.life = 0;
+        this.idle = 0;
+        this.packets = 0;
+        this.bytes = 0;
+        this.id = FlowId.valueOf(this.hashCode());
+        this.created = System.currentTimeMillis();
+    }
+
+    // TODO: Decide whether to take the flowId from the underlying flowentry.
+    public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
+            TrafficTreatment treatment, int priority,
+            long life, long idle, long packets, long bytes) {
+        this(deviceId, selector, treatment, priority);
+        this.life = life;
+        this.idle = idle;
+        this.packets = packets;
+        this.bytes = bytes;
+    }
+
+
+    @Override
+    public FlowId id() {
+        return id;
     }
 
     @Override
     public int priority() {
-        // is this supposed to be 0?
-        return 0;
+        return priority;
     }
 
     @Override
@@ -37,6 +70,32 @@
     }
 
     @Override
+    public long lifeMillis() {
+        return life;
+    }
+
+    @Override
+    public long idleMillis() {
+        return idle;
+    }
+
+    @Override
+    public long packets() {
+        return packets;
+    }
+
+    @Override
+    public long bytes() {
+        return bytes;
+    }
+
+    @Override
+    /*
+     * The priority and statistics can change on a given treatment and selector
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
     public int hashCode() {
         final int prime = 31;
         int result = prime * this.deviceId().hashCode();
@@ -69,5 +128,16 @@
         return false;
     }
 
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("deviceId", deviceId)
+                .add("priority", priority)
+                .add("selector", selector)
+                .add("treatment", treatment)
+                .add("created", created)
+                .toString();
+    }
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
deleted file mode 100644
index 7fee1c1..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.onlab.onos.net.flow;
-
-/**
- * Represents a flow rule and its associated accumulated metrics.
- */
-public interface FlowEntry extends FlowRule {
-
-    /**
-     * Returns the ID of this flow.
-     *
-     * @return the flow ID
-     */
-    FlowId id();
-
-    /**
-     * Returns the number of milliseconds this flow rule has been applied.
-     *
-     * @return number of millis
-     */
-    long lifeMillis();
-
-    /**
-     * Returns the number of milliseconds this flow rule has been idle.
-     *
-     * @return number of millis
-     */
-    long idleMillis();
-
-    /**
-     * Returns the number of packets this flow rule has matched.
-     *
-     * @return number of packets
-     */
-    long packets();
-
-    /**
-     * Returns the number of bytes this flow rule has matched.
-     *
-     * @return number of bytes
-     */
-    long bytes();
-
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
index 5f44630..f2bc1a0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
@@ -9,6 +9,12 @@
 public interface FlowRule {
 
     //TODO: build cookie value
+    /**
+     * Returns the ID of this flow.
+     *
+     * @return the flow ID
+     */
+    FlowId id();
 
     /**
      * Returns the flow rule priority given in natural order; higher numbers
@@ -40,4 +46,32 @@
      */
     TrafficTreatment treatment();
 
+    /**
+     * Returns the number of milliseconds this flow rule has been applied.
+     *
+     * @return number of millis
+     */
+    long lifeMillis();
+
+    /**
+     * Returns the number of milliseconds this flow rule has been idle.
+     *
+     * @return number of millis
+     */
+    long idleMillis();
+
+    /**
+     * Returns the number of packets this flow rule has matched.
+     *
+     * @return number of packets
+     */
+    long packets();
+
+    /**
+     * Returns the number of bytes this flow rule has matched.
+     *
+     * @return number of bytes
+     */
+    long bytes();
+
 }
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 184479b..0277695 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
@@ -25,5 +25,4 @@
      */
     void removeFlowRule(FlowRule... flowRules);
 
-
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
index ba75ae9..d2b9432 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
@@ -21,7 +21,7 @@
      * @param deviceId device identifier
      * @return collection of flow rules
      */
-    Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
+    Iterable<FlowRule> getFlowEntries(DeviceId deviceId);
 
     // TODO: add createFlowRule factory method and execute operations method
 
@@ -34,7 +34,7 @@
      * throws SomeKindOfException that indicates which ones were applied and
      *                  which ones failed
      */
-    List<FlowEntry> applyFlowRules(FlowRule... flowRules);
+    List<FlowRule> applyFlowRules(FlowRule... flowRules);
 
     /**
      * Removes the specified flow rules from their respective devices. If the
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java b/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
index f8cdb6b..6cf91a6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
@@ -1,6 +1,7 @@
 package org.onlab.onos.net.provider;
 
 import com.google.common.collect.ImmutableSet;
+import org.onlab.onos.net.DeviceId;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -20,6 +21,7 @@
 
     private final Map<ProviderId, P> providers = new HashMap<>();
     private final Map<ProviderId, S> services = new HashMap<>();
+    private final Map<String, P> providersByScheme = new HashMap<>();
 
     /**
      * Creates a new provider service bound to the specified provider.
@@ -65,4 +67,14 @@
         return providers.get(providerId);
     }
 
+    /**
+     * Returns the provider for the specified device ID based on URI scheme.
+     *
+     * @param deviceId device identifier
+     * @return provider bound to the URI scheme
+     */
+    protected synchronized P getProvider(DeviceId deviceId) {
+        return providersByScheme.get(deviceId.uri().getScheme());
+    }
+
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
index a945354..e9af0eb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
@@ -9,6 +9,7 @@
  */
 public class ProviderId {
 
+    private final String scheme;
     private final String id;
 
     /**
@@ -16,15 +17,26 @@
      * The providers are expected to follow the reverse DNS convention, e.g.
      * {@code org.onlab.onos.provider.of.device}
      *
-     * @param id string identifier
+     * @param scheme device URI scheme to which this provider is bound, e.g. "of", "snmp"
+     * @param id     string identifier
      */
-    public ProviderId(String id) {
+    public ProviderId(String scheme, String id) {
+        this.scheme = scheme;
         this.id = id;
     }
 
+    /**
+     * Returns the device URI scheme to which this provider is bound.
+     *
+     * @return device URI scheme
+     */
+    public String scheme() {
+        return scheme;
+    }
+
     @Override
     public int hashCode() {
-        return Objects.hash(id);
+        return Objects.hash(scheme, id);
     }
 
     @Override
@@ -36,12 +48,13 @@
             return false;
         }
         final ProviderId other = (ProviderId) obj;
-        return Objects.equals(this.id, other.id);
+        return Objects.equals(this.scheme, other.scheme) &&
+                Objects.equals(this.id, other.id);
     }
 
     @Override
     public String toString() {
-        return toStringHelper(this).add("id", id).toString();
+        return toStringHelper(this).add("scheme", scheme).add("id", id).toString();
     }
 
 }
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
index c37a15c..ea1c8fd 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
@@ -13,7 +13,7 @@
  */
 public class DefaultDeviceTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final DeviceId DID2 = deviceId("of:bar");
     private static final String MFR = "whitebox";
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
index a19f969..b3891f1 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
@@ -15,7 +15,7 @@
  */
 public class DefaultEdgeLinkTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final HostId HID1 = hostId("nic:foobar");
     private static final HostId HID2 = hostId("nic:barfoo");
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultLinkTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultLinkTest.java
index ec8511e..a62c822 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultLinkTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultLinkTest.java
@@ -15,7 +15,7 @@
  */
 public class DefaultLinkTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final DeviceId DID2 = deviceId("of:bar");
     private static final PortNumber P1 = portNumber(1);
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultPortTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultPortTest.java
index e9d3da6..1069fd1 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultPortTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultPortTest.java
@@ -14,7 +14,7 @@
  */
 public class DefaultPortTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final DeviceId DID2 = deviceId("of:bar");
     private static final PortNumber P1 = portNumber(1);
diff --git a/core/api/src/test/java/org/onlab/onos/net/TestDeviceParams.java b/core/api/src/test/java/org/onlab/onos/net/TestDeviceParams.java
index afaf23e..7471a2d 100644
--- a/core/api/src/test/java/org/onlab/onos/net/TestDeviceParams.java
+++ b/core/api/src/test/java/org/onlab/onos/net/TestDeviceParams.java
@@ -17,7 +17,7 @@
  */
 public abstract class TestDeviceParams {
 
-    protected static final ProviderId PID = new ProviderId("foo");
+    protected static final ProviderId PID = new ProviderId("of", "foo");
     protected static final DeviceId DID1 = deviceId("of:foo");
     protected static final DeviceId DID2 = deviceId("of:bar");
     protected static final MacAddress MAC1 = MacAddress.valueOf("00:11:00:00:00:01");
diff --git a/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java b/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
index 7175308..df19365 100644
--- a/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
@@ -18,7 +18,7 @@
 public class DeviceEventTest extends AbstractEventTest {
 
     private Device createDevice() {
-        return new DefaultDevice(new ProviderId("foo"), deviceId("of:foo"),
+        return new DefaultDevice(new ProviderId("of", "foo"), deviceId("of:foo"),
                 Device.Type.SWITCH, "box", "hw", "sw", "sn");
     }
 
diff --git a/core/api/src/test/java/org/onlab/onos/net/host/HostEventTest.java b/core/api/src/test/java/org/onlab/onos/net/host/HostEventTest.java
index a0d6848..561b041 100644
--- a/core/api/src/test/java/org/onlab/onos/net/host/HostEventTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/host/HostEventTest.java
@@ -34,7 +34,7 @@
         HostId hid = HostId.hostId(mac, vlan);
 
         return new DefaultHost(
-                new ProviderId("foo"), hid, mac, vlan, loc, ipset);
+                new ProviderId("of", "foo"), hid, mac, vlan, loc, ipset);
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/link/LinkEventTest.java b/core/api/src/test/java/org/onlab/onos/net/link/LinkEventTest.java
index dae9f85..aa95a42 100644
--- a/core/api/src/test/java/org/onlab/onos/net/link/LinkEventTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/link/LinkEventTest.java
@@ -16,7 +16,7 @@
 public class LinkEventTest extends AbstractEventTest {
 
     private Link createLink() {
-        return new DefaultLink(new ProviderId("foo"),
+        return new DefaultLink(new ProviderId("of", "foo"),
                                new ConnectPoint(deviceId("of:foo"), portNumber(1)),
                                new ConnectPoint(deviceId("of:bar"), portNumber(2)),
                                Link.Type.INDIRECT);
diff --git a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
index abbe5be..37bee71 100644
--- a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
@@ -28,14 +28,14 @@
         TestProviderRegistry registry = new TestProviderRegistry();
         assertEquals("incorrect provider count", 0, registry.getProviders().size());
 
-        ProviderId fooId = new ProviderId("foo");
+        ProviderId fooId = new ProviderId("of", "foo");
         TestProvider pFoo = new TestProvider(fooId);
         TestProviderService psFoo = registry.register(pFoo);
         assertEquals("incorrect provider count", 1, registry.getProviders().size());
         assertThat("provider not found", registry.getProviders().contains(fooId));
         assertEquals("incorrect provider", psFoo.provider(), pFoo);
 
-        ProviderId barId = new ProviderId("bar");
+        ProviderId barId = new ProviderId("of", "bar");
         TestProvider pBar = new TestProvider(barId);
         TestProviderService psBar = registry.register(pBar);
         assertEquals("incorrect provider count", 2, registry.getProviders().size());
@@ -52,7 +52,7 @@
     @Test(expected = IllegalStateException.class)
     public void duplicateRegistration() {
         TestProviderRegistry registry = new TestProviderRegistry();
-        TestProvider pFoo = new TestProvider(new ProviderId("foo"));
+        TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
         registry.register(pFoo);
         registry.register(pFoo);
     }
@@ -60,13 +60,13 @@
     @Test
     public void voidUnregistration() {
         TestProviderRegistry registry = new TestProviderRegistry();
-        registry.unregister(new TestProvider(new ProviderId("foo")));
+        registry.unregister(new TestProvider(new ProviderId("of", "foo")));
     }
 
     @Test(expected = IllegalStateException.class)
     public void unregistration() {
         TestProviderRegistry registry = new TestProviderRegistry();
-        TestProvider pFoo = new TestProvider(new ProviderId("foo"));
+        TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
         TestProviderService psFoo = registry.register(pFoo);
         registry.unregister(pFoo);
         psFoo.checkValidity();
diff --git a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderTest.java b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderTest.java
index 745aebc..c2e5cfc 100644
--- a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderTest.java
@@ -11,7 +11,7 @@
 
     @Test
     public void basics() {
-        ProviderId id = new ProviderId("foo.bar");
+        ProviderId id = new ProviderId("of", "foo.bar");
         TestProvider provider = new TestProvider(id);
         assertEquals("incorrect id", id, provider.id());
     }
diff --git a/core/api/src/test/java/org/onlab/onos/net/provider/ProviderIdTest.java b/core/api/src/test/java/org/onlab/onos/net/provider/ProviderIdTest.java
index 1c05507..1501eb9 100644
--- a/core/api/src/test/java/org/onlab/onos/net/provider/ProviderIdTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/provider/ProviderIdTest.java
@@ -11,8 +11,9 @@
     @Test
     public void basics() {
         new EqualsTester()
-                .addEqualityGroup(new ProviderId("foo"), new ProviderId("foo"))
-                .addEqualityGroup(new ProviderId("bar"))
+                .addEqualityGroup(new ProviderId("of", "foo"), new ProviderId("of", "foo"))
+                .addEqualityGroup(new ProviderId("snmp", "foo"), new ProviderId("snmp", "foo"))
+                .addEqualityGroup(new ProviderId("of", "bar"))
                 .testEquals();
     }
 
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java
index b7c5b15..7003271 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java
@@ -17,7 +17,6 @@
 import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.DeviceService;
-import org.onlab.onos.net.flow.FlowEntry;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleListener;
@@ -62,13 +61,13 @@
     }
 
     @Override
-    public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+    public Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
         return store.getFlowEntries(deviceId);
     }
 
     @Override
-    public List<FlowEntry> applyFlowRules(FlowRule... flowRules) {
-        List<FlowEntry> entries = new ArrayList<FlowEntry>();
+    public List<FlowRule> applyFlowRules(FlowRule... flowRules) {
+        List<FlowRule> entries = new ArrayList<FlowRule>();
 
         for (int i = 0; i < flowRules.length; i++) {
             FlowRule f = flowRules[i];
@@ -159,7 +158,7 @@
         }
 
         @Override
-        public void pushFlowMetrics(Iterable<FlowEntry> flowEntries) {
+        public void pushFlowMetrics(Iterable<FlowRule> flowEntries) {
             // TODO Auto-generated method stub
 
         }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java
index 4f23ff4..dd8dce8 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java
@@ -1,8 +1,7 @@
 package org.onlab.onos.net.trivial.flow.impl;
 
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowEntry;
-import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.DefaultFlowRule;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 
@@ -18,7 +17,7 @@
 public class SimpleFlowRuleStore {
 
     // store entries as a pile of rules, no info about device tables
-    private final Multimap<DeviceId, FlowEntry> flowEntries = HashMultimap.create();
+    private final Multimap<DeviceId, FlowRule> flowEntries = HashMultimap.create();
 
     /**
      * Returns the flow entries associated with a device.
@@ -26,19 +25,19 @@
      * @param deviceId the device ID
      * @return the flow entries
      */
-    Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+    Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
         return ImmutableSet.copyOf(flowEntries.get(deviceId));
     }
 
     /**
-     * Stores a new flow rule, and generates a FlowEntry for it.
+     * Stores a new flow rule, and generates a FlowRule for it.
      *
      * @param rule the flow rule to add
      * @return a flow entry
      */
-    FlowEntry storeFlowRule(FlowRule rule) {
+    FlowRule storeFlowRule(FlowRule rule) {
         DeviceId did = rule.deviceId();
-        FlowEntry entry = new DefaultFlowEntry(did,
+        FlowRule entry = new DefaultFlowRule(did,
                 rule.selector(), rule.treatment(), rule.priority());
         flowEntries.put(did, entry);
         return entry;
@@ -53,20 +52,14 @@
     FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
         DeviceId did = rule.deviceId();
 
-        FlowEntry entry = new DefaultFlowEntry(
-                did,
-                rule.selector(),
-                rule.treatment(),
-                rule.priority());
-
         // check if this new rule is an update to an existing entry
-        for (FlowEntry fe : flowEntries.get(did)) {
-            if (entry.equals(fe)) {
-                // TODO update the stats on this flowEntry?
+        for (FlowRule fe : flowEntries.get(did)) {
+            if (rule.equals(fe)) {
+                // TODO update the stats on this FlowRule?
                 return null;
             }
         }
-        flowEntries.put(did, entry);
+        flowEntries.put(did, rule);
         return new FlowRuleEvent(RULE_ADDED, rule);
     }
 
@@ -77,10 +70,8 @@
      */
     FlowRuleEvent removeFlowRule(FlowRule rule) {
 
-        FlowEntry rem = new DefaultFlowEntry(rule.deviceId(),
-                rule.selector(), rule.treatment(), rule.priority());
         synchronized (this) {
-            if (flowEntries.remove(rem.deviceId(), rem)) {
+            if (flowEntries.remove(rule.deviceId(), rule)) {
                 return new FlowRuleEvent(RULE_REMOVED, rule);
             } else {
                 return null;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
index 7213497..efbe6af 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
@@ -45,7 +45,7 @@
     private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
             new TarjanGraphSearch<>();
 
-    private static final ProviderId PID = new ProviderId("org.onlab.onos.net");
+    private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.net");
 
     private final long time;
     private final TopologyGraph graph;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java
index b6efba9..6e6a917 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java
@@ -74,7 +74,7 @@
      * Creates a provider with the supplier identifier.
      */
     public DefaultTopologyProvider() {
-        super(new ProviderId("org.onlab.onos.provider.topology"));
+        super(new ProviderId("core", "org.onlab.onos.provider.topology"));
     }
 
     @Activate
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java
index f495057..5fe7793 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java
@@ -45,7 +45,7 @@
 
     private static final String ELEMENT_ID_NULL = "Element ID cannot be null";
 
-    private static final ProviderId PID = new ProviderId("org.onlab.onos.core");
+    private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core");
     private static final PortNumber P0 = PortNumber.portNumber(0);
 
     private static final EdgeLink NOT_HOST = new NotHost();
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java
index 4c1aff4..4d531d3 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java
@@ -38,7 +38,7 @@
  */
 public class SimpleDeviceManagerTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final DeviceId DID2 = deviceId("of:bar");
     private static final String MFR = "whitebox";
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManagerTest.java
index bb0a496..0bee5b3 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManagerTest.java
@@ -4,6 +4,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
+import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -21,9 +23,7 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.device.DeviceListener;
 import org.onlab.onos.net.device.DeviceService;
-import org.onlab.onos.net.flow.DefaultFlowEntry;
 import org.onlab.onos.net.flow.DefaultFlowRule;
-import org.onlab.onos.net.flow.FlowEntry;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleListener;
@@ -41,14 +41,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
-import static org.onlab.onos.net.flow.FlowRuleEvent.Type.*;
-
 /**
  * Test codifying the flow rule service & flow rule provider service contracts.
  */
 public class SimpleFlowRuleManagerTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID = DeviceId.deviceId("of:001");
     private static final Device DEV = new DefaultDevice(
             PID, DID, Type.SWITCH, "", "", "", "");
@@ -81,7 +79,7 @@
     public void tearDown() {
         registry.unregister(provider);
         assertFalse("provider should not be registered",
-                    registry.getProviders().contains(provider.id()));
+                registry.getProviders().contains(provider.id()));
         service.removeListener(listener);
         mgr.deactivate();
         mgr.eventDispatcher = null;
@@ -91,7 +89,7 @@
     private FlowRule flowRule(int tsval, int trval) {
         TestSelector ts = new TestSelector(tsval);
         TestTreatment tr = new TestTreatment(trval);
-        return new DefaultFlowRule(DID, ts, tr);
+        return new DefaultFlowRule(DID, ts, tr, 0);
     }
 
     private void addFlowRule(int hval) {
@@ -142,14 +140,14 @@
         FlowRule r3 = flowRule(1, 3);
 
         //current FlowRules always return 0. FlowEntries inherit the value
-        FlowEntry e1 = new DefaultFlowEntry(DID, ts, r1.treatment(), 0);
-        FlowEntry e2 = new DefaultFlowEntry(DID, ts, r2.treatment(), 0);
-        FlowEntry e3 = new DefaultFlowEntry(DID, ts, r3.treatment(), 0);
-        List<FlowEntry> fel = Lists.newArrayList(e1, e2, e3);
+        FlowRule e1 = new DefaultFlowRule(DID, ts, r1.treatment(), 0);
+        FlowRule e2 = new DefaultFlowRule(DID, ts, r2.treatment(), 0);
+        FlowRule e3 = new DefaultFlowRule(DID, ts, r3.treatment(), 0);
+        List<FlowRule> fel = Lists.newArrayList(e1, e2, e3);
 
         assertTrue("store should be empty",
                 Sets.newHashSet(service.getFlowEntries(DID)).isEmpty());
-        List<FlowEntry> ret = mgr.applyFlowRules(r1, r2, r3);
+        List<FlowRule> ret = mgr.applyFlowRules(r1, r2, r3);
         assertEquals("3 rules should exist", 3, flowCount());
         assertTrue("3 entries should result", fel.containsAll(ret));
     }
@@ -255,17 +253,12 @@
         public void removeFlowRule(FlowRule... flowRules) {
         }
 
-        @Override
-        public Iterable<FlowEntry> getFlowMetrics(DeviceId deviceId) {
-            return null;
-        }
-
     }
 
     private class TestSelector implements TrafficSelector {
 
         //for controlling hashcode uniqueness;
-        private int testval;
+        private final int testval;
 
         public TestSelector(int val) {
             testval = val;
@@ -293,7 +286,7 @@
     private class TestTreatment implements TrafficTreatment {
 
         //for controlling hashcode uniqueness;
-        private int testval;
+        private final int testval;
 
         public TestTreatment(int val) {
             testval = val;
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java
index b1b825e..67c14b0 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java
@@ -42,7 +42,7 @@
  */
 public class SimpleHostManagerTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
 
     private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
     private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java
index 3702b0a..93610b5 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java
@@ -40,7 +40,7 @@
  */
 public class SimpleLinkManagerTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final DeviceId DID2 = deviceId("of:bar");
     private static final DeviceId DID3 = deviceId("of:goo");
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java
index 2520602..1fe33f6 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java
@@ -29,7 +29,7 @@
  */
 public class DefaultTopologyTest {
 
-    public static final ProviderId PID = new ProviderId("foo.bar");
+    public static final ProviderId PID = new ProviderId("of", "foo.bar");
 
     public static final DeviceId D1 = deviceId("of:1");
     public static final DeviceId D2 = deviceId("of:2");
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java
index 1480cec..b0b1eed 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java
@@ -43,7 +43,7 @@
  */
 public class SimpleTopologyManagerTest {
 
-    private static final ProviderId PID = new ProviderId("foo");
+    private static final ProviderId PID = new ProviderId("of", "foo");
 
     private SimpleTopologyManager mgr;
 
diff --git a/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
index 7fdf8cb..1271bc8 100644
--- a/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -60,7 +60,7 @@
      * Creates an OpenFlow device provider.
      */
     public OpenFlowDeviceProvider() {
-        super(new ProviderId("org.onlab.onos.provider.openflow"));
+        super(new ProviderId("of", "org.onlab.onos.provider.openflow"));
     }
 
     @Activate
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 4899a0e..73e00bb 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
@@ -62,7 +62,7 @@
      * Creates an OpenFlow host provider.
      */
     public OpenFlowRuleProvider() {
-        super(new ProviderId("org.onlab.onos.provider.openflow"));
+        super(new ProviderId("of", "org.onlab.onos.provider.openflow"));
     }
 
     @Activate
@@ -131,11 +131,11 @@
             case FLOW_REMOVED:
                 //TODO: make this better
                 OFFlowRemoved removed = (OFFlowRemoved) msg;
-                FlowRule fr = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), null, null);
+                FlowRule fr = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), null, null, 0);
                 providerService.flowRemoved(fr);
                 break;
             case STATS_REPLY:
-                pushFlowMetrics((OFStatsReply) msg);
+                pushFlowMetrics(dpid, (OFStatsReply) msg);
                 break;
             case BARRIER_REPLY:
             case ERROR:
@@ -145,18 +145,16 @@
 
         }
 
-        private void pushFlowMetrics(OFStatsReply stats) {
+        private void pushFlowMetrics(Dpid dpid, OFStatsReply stats) {
             if (stats.getStatsType() != OFStatsType.FLOW) {
                 return;
             }
             final OFFlowStatsReply replies = (OFFlowStatsReply) stats;
             final List<FlowRule> entries = Lists.newLinkedList();
             for (OFFlowStatsEntry reply : replies.getEntries()) {
-                entries.add(new FlowRuleBuilder(reply).build());
+                entries.add(new FlowRuleBuilder(dpid, reply).build());
             }
             providerService.pushFlowMetrics(entries);
-
-
         }
 
     }
diff --git a/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java b/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
index 28408ab..3fab4c7 100644
--- a/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
+++ b/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
@@ -61,7 +61,7 @@
      * Creates an OpenFlow host provider.
      */
     public OpenFlowHostProvider() {
-        super(new ProviderId("org.onlab.onos.provider.openflow"));
+        super(new ProviderId("of", "org.onlab.onos.provider.openflow"));
     }
 
     @Activate
diff --git a/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java b/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
index 1727d1d..0571922 100644
--- a/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
+++ b/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
@@ -55,7 +55,7 @@
      * Creates an OpenFlow link provider.
      */
     public OpenFlowLinkProvider() {
-        super(new ProviderId("org.onlab.onos.provider.openflow"));
+        super(new ProviderId("of", "org.onlab.onos.provider.openflow"));
     }
 
     @Activate
diff --git a/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java b/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
index 77fb689..bc5b892 100644
--- a/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
+++ b/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
@@ -48,7 +48,7 @@
      * Creates an OpenFlow link provider.
      */
     public OpenFlowPacketProvider() {
-        super(new ProviderId("org.onlab.onos.provider.openflow"));
+        super(new ProviderId("of", "org.onlab.onos.provider.openflow"));
     }
 
     @Activate
diff --git a/tools/build/onos-package b/tools/build/onos-package
index 5918306..72486ad 100755
--- a/tools/build/onos-package
+++ b/tools/build/onos-package
@@ -27,23 +27,37 @@
 # Stage the ONOS admin scripts and patch in Karaf service wrapper extras
 cp -r $ONOS_ROOT/tools/package/bin .
 cp -r $ONOS_ROOT/tools/package/wrapper/* $KARAF_DIST
+cp -r $ONOS_ROOT/tools/package/etc/* $KARAF_DIST/etc
 
 # Stage the ONOS bundles
 mkdir -p $KARAF_DIST/system/org/onlab 
 cp -r $M2_REPO/org/onlab $KARAF_DIST/system/org/
 
+# Wrapper & Cellar Patching ----------------------------------------------------
+
+# Patch the Apache Karaf distribution file to add Cellar features repository
+perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.apache.karaf.cellar/apache-karaf-cellar/3.0.0/xml/features|" \
+    $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
+
+# Patch the Apache Karaf distribution file to load ONOS features
+perl -pi.old -e 's|^(featuresBoot=.*)|\1,wrapper,cellar|' \
+    $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
+
+# ONOS Patching ----------------------------------------------------------------
+
 # Patch the Apache Karaf distribution file to add ONOS features repository
 perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-features/$ONOS_VERSION/xml/features|" \
     $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
 
 # Patch the Apache Karaf distribution file to load ONOS features
-perl -pi.old -e 's|^(featuresBoot=.*)|\1,wrapper,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-tvue|' \
+perl -pi.old -e 's|^(featuresBoot=.*)|\1,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-tvue|' \
     $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
 
 # Patch the Apache Karaf distribution with ONOS branding bundle
 cp $M2_REPO/org/onlab/onos/onos-branding/$ONOS_VERSION/onos-branding-*.jar \
     $ONOS_STAGE/$KARAF_DIST/lib
 
+
 # Now package up the ONOS tar file
 cd $ONOS_STAGE_ROOT
 COPYFILE_DISABLE=1 tar zcf $ONOS_TAR $ONOS_BITS
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index cc19a4d..de0ed1e 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -1,5 +1,6 @@
 #!/bin/bash
 # ONOS developer BASH profile conveniences
+# Simply include in your own .bash_aliases or .bash_profile
 
 # Root of the ONOS source tree
 export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
@@ -42,6 +43,23 @@
 alias gui='open http://localhost:8181/onos/tvue'
 
 
+# Test related conveniences
+
+# Default virtual box ONOS instances 1,2 & 3
+export OC1="192.168.56.101"
+export OC2="192.168.56.102"
+export OC3="192.168.56.103"
+
+# Default instance is #1
+export OCI="$OC1"
+
+# SSH to a specified ONOS instance
+function sshctl {
+    [ -n "$1" ] && OCI=$1 && shift
+    ssh -Y sdn@$OCI "$@"
+}
+
+
 # Miscellaneous
 function spy {
     ps -ef | egrep "$@" | grep -v egrep
diff --git a/tools/package/etc/hazelcast.xml b/tools/package/etc/hazelcast.xml
new file mode 100644
index 0000000..5cb41ba
--- /dev/null
+++ b/tools/package/etc/hazelcast.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.2.xsd"
+           xmlns="http://www.hazelcast.com/schema/config"
+           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <group>
+        <name>onos</name>
+        <password>rocks</password>
+    </group>
+    <management-center enabled="false">http://localhost:8080/mancenter</management-center>
+    <network>
+        <port auto-increment="true" port-count="100">5701</port>
+        <outbound-ports>
+            <!--
+                Allowed port range when connecting to other nodes.
+                0 or * means use system provided port.
+            -->
+            <ports>0</ports>
+        </outbound-ports>
+        <join>
+            <multicast enabled="true">
+                <multicast-group>224.2.2.3</multicast-group>
+                <multicast-port>54327</multicast-port>
+            </multicast>
+            <tcp-ip enabled="false">
+                <interface>127.0.0.1</interface>
+            </tcp-ip>
+            <aws enabled="false">
+                <access-key>my-access-key</access-key>
+                <secret-key>my-secret-key</secret-key>
+                <!--optional, default is us-east-1 -->
+                <region>us-west-1</region>
+                <!--optional, default is ec2.amazonaws.com. If set, region shouldn't be set as it will override this property -->
+                <host-header>ec2.amazonaws.com</host-header>
+                <!-- optional, only instances belonging to this group will be discovered, default will try all running instances -->
+                <security-group-name>hazelcast-sg</security-group-name>
+                <tag-key>type</tag-key>
+                <tag-value>hz-nodes</tag-value>
+            </aws>
+        </join>
+        <interfaces enabled="true">
+            <interface>10.1.9.*</interface>
+        </interfaces>
+        <ssl enabled="false"/>
+        <socket-interceptor enabled="false"/>
+        <symmetric-encryption enabled="false">
+            <!--
+               encryption algorithm such as
+               DES/ECB/PKCS5Padding,
+               PBEWithMD5AndDES,
+               AES/CBC/PKCS5Padding,
+               Blowfish,
+               DESede
+            -->
+            <algorithm>PBEWithMD5AndDES</algorithm>
+            <!-- salt value to use when generating the secret key -->
+            <salt>thesalt</salt>
+            <!-- pass phrase to use when generating the secret key -->
+            <password>thepass</password>
+            <!-- iteration count to use when generating the secret key -->
+            <iteration-count>19</iteration-count>
+        </symmetric-encryption>
+    </network>
+    <partition-group enabled="false"/>
+    <executor-service>
+        <pool-size>16</pool-size>
+        <!-- Queue capacity. 0 means Integer.MAX_VALUE -->
+        <queue-capacity>0</queue-capacity>
+    </executor-service>
+    <queue name="default">
+        <!--
+            Maximum size of the queue. When a JVM's local queue size reaches the maximum,
+            all put/offer operations will get blocked until the queue size
+            of the JVM goes down below the maximum.
+            Any integer between 0 and Integer.MAX_VALUE. 0 means
+            Integer.MAX_VALUE. Default is 0.
+        -->
+        <max-size>0</max-size>
+        <!--
+            Number of backups. If 1 is set as the backup-count for example,
+            then all entries of the map will be copied to another JVM for
+            fail-safety. 0 means no backup.
+        -->
+        <backup-count>1</backup-count>
+        <!--
+            Number of async backups. 0 means no backup.
+        -->
+        <async-backup-count>0</async-backup-count>
+        <empty-queue-ttl>-1</empty-queue-ttl>
+    </queue>
+
+    <map name="default">
+        <!--
+            Data type that will be used for storing recordMap.
+            Possible values:
+                BINARY (default): keys and values will be stored as binary data
+                OBJECT : values will be stored in their object forms
+                OFFHEAP : values will be stored in non-heap region of JVM
+        -->
+        <in-memory-format>BINARY</in-memory-format>
+        <!--
+            Number of backups. If 1 is set as the backup-count for example,
+            then all entries of the map will be copied to another JVM for
+            fail-safety. 0 means no backup.
+        -->
+        <backup-count>1</backup-count>
+        <!--
+            Number of async backups. 0 means no backup.
+        -->
+        <async-backup-count>0</async-backup-count>
+        <!--
+            Maximum number of seconds for each entry to stay in the map. Entries that are
+            older than <time-to-live-seconds> and not updated for <time-to-live-seconds>
+            will get automatically evicted from the map.
+            Any integer between 0 and Integer.MAX_VALUE. 0 means infinite. Default is 0.
+        -->
+        <time-to-live-seconds>0</time-to-live-seconds>
+        <!--
+            Maximum number of seconds for each entry to stay idle in the map. Entries that are
+            idle(not touched) for more than <max-idle-seconds> will get
+            automatically evicted from the map. Entry is touched if get, put or containsKey is called.
+            Any integer between 0 and Integer.MAX_VALUE. 0 means infinite. Default is 0.
+        -->
+        <max-idle-seconds>0</max-idle-seconds>
+        <!--
+            Valid values are:
+            NONE (no eviction),
+            LRU (Least Recently Used),
+            LFU (Least Frequently Used).
+            NONE is the default.
+        -->
+        <eviction-policy>NONE</eviction-policy>
+        <!--
+            Maximum size of the map. When max size is reached,
+            map is evicted based on the policy defined.
+            Any integer between 0 and Integer.MAX_VALUE. 0 means
+            Integer.MAX_VALUE. Default is 0.
+        -->
+        <max-size policy="PER_NODE">0</max-size>
+        <!--
+            When max. size is reached, specified percentage of
+            the map will be evicted. Any integer between 0 and 100.
+            If 25 is set for example, 25% of the entries will
+            get evicted.
+        -->
+        <eviction-percentage>25</eviction-percentage>
+        <!--
+            While recovering from split-brain (network partitioning),
+            map entries in the small cluster will merge into the bigger cluster
+            based on the policy set here. When an entry merge into the
+            cluster, there might an existing entry with the same key already.
+            Values of these entries might be different for that same key.
+            Which value should be set for the key? Conflict is resolved by
+            the policy set here. Default policy is PutIfAbsentMapMergePolicy
+
+            There are built-in merge policies such as
+            com.hazelcast.map.merge.PassThroughMergePolicy; entry will be added if there is no existing entry for the key.
+            com.hazelcast.map.merge.PutIfAbsentMapMergePolicy ; entry will be added if the merging entry doesn't exist in the cluster.
+            com.hazelcast.map.merge.HigherHitsMapMergePolicy ; entry with the higher hits wins.
+            com.hazelcast.map.merge.LatestUpdateMapMergePolicy ; entry with the latest update wins.
+        -->
+        <merge-policy>com.hazelcast.map.merge.PassThroughMergePolicy</merge-policy>
+    </map>
+
+    <multimap name="default">
+        <backup-count>1</backup-count>
+        <value-collection-type>SET</value-collection-type>
+    </multimap>
+
+    <multimap name="default">
+        <backup-count>1</backup-count>
+        <value-collection-type>SET</value-collection-type>
+    </multimap>
+
+    <list name="default">
+        <backup-count>1</backup-count>
+    </list>
+
+    <set name="default">
+        <backup-count>1</backup-count>
+    </set>
+
+    <jobtracker name="default">
+        <max-thread-size>0</max-thread-size>
+        <!-- Queue size 0 means number of partitions * 2 -->
+        <queue-size>0</queue-size>
+        <retry-count>0</retry-count>
+        <chunk-size>1000</chunk-size>
+        <communicate-stats>true</communicate-stats>
+        <topology-changed-strategy>CANCEL_RUNNING_OPERATION</topology-changed-strategy>
+    </jobtracker>
+
+    <semaphore name="default">
+        <initial-permits>0</initial-permits>
+        <backup-count>1</backup-count>
+        <async-backup-count>0</async-backup-count>
+    </semaphore>
+
+    <serialization>
+        <portable-version>0</portable-version>
+    </serialization>
+
+    <services enable-defaults="true" />
+</hazelcast>
diff --git a/tools/test/bin/onos-log b/tools/test/bin/onos-log
new file mode 100755
index 0000000..611d6d7
--- /dev/null
+++ b/tools/test/bin/onos-log
@@ -0,0 +1,18 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Monitors remote ONOS log file.
+#-------------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+remote=$ONOS_USER@${1:-$OCI}
+
+LOG=$ONOS_INSTALL_DIR/log/karaf.log
+
+ssh $remote "
+    while true; do
+        [ ! -f $LOG ] && sleep 2 && continue
+        tail -n 512 --follow=name $LOG --sleep-interval 2
+    done
+"