Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
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 9cb36e0..98c14f5 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
@@ -5,14 +5,31 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.Instructions;
 import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.packet.InboundPacket;
+import org.onlab.onos.net.packet.PacketContext;
 import org.onlab.onos.net.packet.PacketProcessor;
 import org.onlab.onos.net.packet.PacketService;
 import org.onlab.onos.net.topology.TopologyService;
+import org.slf4j.Logger;
 
-@Component
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Sample reactive forwarding application.
+ */
+@Component(immediate = true)
 public class ReactiveForwarding {
 
+    private final Logger log = getLogger(getClass());
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected TopologyService topologyService;
 
@@ -22,18 +39,99 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
-    private ReactivePacketProcessor processor;
+    private ReactivePacketProcessor processor = new ReactivePacketProcessor();
 
     @Activate
     public void activate() {
-        processor = new ReactivePacketProcessor(topologyService, hostService);
         packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 1);
+        log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
         packetService.removeProcessor(processor);
         processor = null;
+        log.info("Stopped");
     }
+
+
+    /**
+     * Packet processor responsible for forwarding packets along their paths.
+     */
+    private class ReactivePacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            InboundPacket pkt = context.inPacket();
+            HostId id = HostId.hostId(pkt.parsed().getDestinationMAC());
+
+            // Do we know who this is for? If not, flood and bail.
+            Host dst = hostService.getHost(id);
+            if (dst == null) {
+                flood(context);
+                return;
+            }
+
+            // Are we on an edge switch that our destination is on? If so,
+            // simply forward out to the destination and bail.
+            if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
+                forward(context, dst.location().port());
+                return;
+            }
+
+            // Otherwise, get a set of paths that lead from here to the
+            // destination edge switch.
+            Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
+                                                       context.inPacket().receivedFrom().deviceId(),
+                                                       dst.location().deviceId());
+            if (paths.isEmpty()) {
+                // If there are no paths, flood and bail.
+                flood(context);
+                return;
+            }
+
+            // Otherwise, pick a path that does not lead back to where we
+            // came from; if no such path, flood and bail.
+            Path path = pickForwardPath(paths, pkt.receivedFrom().port());
+            if (path == null) {
+                log.warn("Doh... don't know where to go...");
+                flood(context);
+                return;
+            }
+
+            // Otherwise forward and be done with it.
+            forward(context, path.src().port());
+        }
+    }
+
+    // Selects a path from the given set that does not lead back to the
+    // specified port.
+    private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
+        for (Path path : paths) {
+            if (!path.src().port().equals(notToPort)) {
+                return path;
+            }
+        }
+        return null;
+    }
+
+    // Floods the specified packet.
+    private void flood(PacketContext context) {
+        boolean canBcast = topologyService.isBroadcastPoint(topologyService.currentTopology(),
+                                                            context.inPacket().receivedFrom());
+        if (canBcast) {
+            forward(context, PortNumber.FLOOD);
+        } else {
+            context.block();
+        }
+    }
+
+    // Forwards the packet to the specified port.
+    private void forward(PacketContext context, PortNumber portNumber) {
+        context.treatmentBuilder().add(Instructions.createOutput(portNumber));
+        context.send();
+    }
+
 }
 
+
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactivePacketProcessor.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactivePacketProcessor.java
deleted file mode 100644
index 4ecb129..0000000
--- a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactivePacketProcessor.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.onlab.onos.fwd;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Set;
-
-import org.onlab.onos.net.Host;
-import org.onlab.onos.net.HostId;
-import org.onlab.onos.net.Path;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.flow.Instructions;
-import org.onlab.onos.net.host.HostService;
-import org.onlab.onos.net.packet.InboundPacket;
-import org.onlab.onos.net.packet.PacketContext;
-import org.onlab.onos.net.packet.PacketProcessor;
-import org.onlab.onos.net.topology.TopologyService;
-import org.onlab.packet.VLANID;
-import org.slf4j.Logger;
-
-public class ReactivePacketProcessor implements PacketProcessor {
-
-    private final Logger log = getLogger(getClass());
-    private final TopologyService topologyService;
-    private final HostService hostService;
-
-
-    public ReactivePacketProcessor(TopologyService topologyService, HostService hostService) {
-        this.topologyService = topologyService;
-        this.hostService = hostService;
-    }
-
-
-    @Override
-    public void process(PacketContext context) {
-        InboundPacket pkt = context.inPacket();
-        HostId id = HostId.hostId(pkt.parsed().getDestinationMAC(), VLANID.vlanId((short) -1));
-        Host dst = hostService.getHost(id);
-        if (dst == null) {
-            flood(context);
-            return;
-        }
-
-        Set<Path> p = null;
-        if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
-            context.treatmentBuilder().add(Instructions.createOutput(dst.location().port()));
-            context.send();
-            return;
-        } else {
-            p = topologyService.getPaths(topologyService.currentTopology(),
-                    context.inPacket().receivedFrom().deviceId(), dst.location().deviceId());
-        }
-
-        if (p.isEmpty()) {
-            flood(context);
-        } else {
-            Path p1 = p.iterator().next();
-            context.treatmentBuilder().add(Instructions.createOutput(p1.src().port()));
-            context.send();
-        }
-
-    }
-
-    private void flood(PacketContext context) {
-        boolean canBcast = topologyService.isBroadcastPoint(topologyService.currentTopology(),
-                context.inPacket().receivedFrom());
-        if (canBcast) {
-            context.treatmentBuilder().add(Instructions.createOutput(PortNumber.FLOOD));
-            context.send();
-        } else {
-            context.block();
-        }
-    }
-
-}
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/package-info.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/package-info.java
new file mode 100644
index 0000000..9624edb
--- /dev/null
+++ b/apps/fwd/src/main/java/org/onlab/onos/fwd/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Trivial application that provides simple form of reactive forwarding.
+ */
+package org.onlab.onos.fwd;
\ No newline at end of file
diff --git a/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java b/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
index 992bbb3..145e94a 100644
--- a/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
+++ b/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
@@ -15,6 +15,7 @@
 import org.onlab.onos.net.topology.TopologyGraph;
 import org.onlab.onos.net.topology.TopologyService;
 import org.onlab.onos.net.topology.TopologyVertex;
+import org.onlab.packet.IPAddress;
 import org.onlab.rest.BaseResource;
 
 import javax.ws.rs.GET;
@@ -54,6 +55,7 @@
         ArrayNode vertexesNode = mapper.createArrayNode();
         for (TopologyVertex vertex : graph.getVertexes()) {
             vertexesNode.add(json(mapper, vertex.deviceId(), 2,
+                                  vertex.deviceId().uri().getSchemeSpecificPart(),
                                   deviceService.isAvailable(vertex.deviceId())));
         }
 
@@ -70,14 +72,17 @@
         // Merge the exterior and interior vertexes and inject host links as
         // the exterior edges.
         for (Host host : hostService.getHosts()) {
-            vertexesNode.add(json(mapper, host.id(), 3, true));
+            Set<IPAddress> ipAddresses = host.ipAddresses();
+            IPAddress ipAddress = ipAddresses.isEmpty() ? null : ipAddresses.iterator().next();
+            String label = ipAddress != null ? ipAddress.toString() : host.mac().toString();
+            vertexesNode.add(json(mapper, host.id(), 3, label, true));
             edgesNode.add(json(mapper, 1, host.location(), new ConnectPoint(host.id(), portNumber(-1))));
         }
 
         // Now put the vertexes and edges into a root node and ship them off
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.put("vertexes", vertexesNode);
-        rootNode.put("edges", edgesNode);
+        rootNode.set("vertexes", vertexesNode);
+        rootNode.set("edges", edgesNode);
         return Response.ok(rootNode.toString()).build();
     }
 
@@ -126,12 +131,12 @@
         return aggLinks;
     }
 
-
     // Produces JSON for a graph vertex.
     private ObjectNode json(ObjectMapper mapper, ElementId id, int group,
-                            boolean isOnline) {
+                            String label, boolean isOnline) {
         return mapper.createObjectNode()
                 .put("name", id.uri().toString())
+                .put("label", label)
                 .put("group", group)
                 .put("online", isOnline);
     }
diff --git a/apps/tvue/src/main/webapp/index.html b/apps/tvue/src/main/webapp/index.html
index 04abfa4..9c551fd 100644
--- a/apps/tvue/src/main/webapp/index.html
+++ b/apps/tvue/src/main/webapp/index.html
@@ -129,7 +129,7 @@
         }
 
         function dragstart(d) {
-            d3.select(this).classed("fixed", d.fixed = true);
+            // d3.select(this).classed("fixed", d.fixed = true);
         }
 
 
@@ -147,8 +147,8 @@
                     }
                     return false;
                 }
-                node = {"id": vertex.name, "group": vertex.group,
-                    "online": vertex.online, "stamp": stamp};
+                node = {"id": vertex.name, "label": vertex.label,
+                    "group": vertex.group, "online": vertex.online, "stamp": stamp};
                 nodes.push(node);
                 topo.vertexes[vertex.name] = node;
                 update();
@@ -239,6 +239,8 @@
                             targetNode = aux;
                         } else if (d3.event.keyCode == 70) {
                             nextPath();
+                        } else if (d3.event.keyCode == 67 && selectedNode) {
+                            selectedNode.fixed = !selectedNode.fixed;
                         }
 
                         d3.selectAll(".nodeStrokeClass").attr("fill", fillColor);
@@ -288,7 +290,7 @@
                         .attr("class", "textClass")
                         .attr("x", 20)
                         .attr("y", ".31em")
-                        .text(function (d) { return d.id; });
+                        .text(function (d) { return d.label; });
 
                 node.exit().remove();
 
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
new file mode 100644
index 0000000..51a0fce
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
@@ -0,0 +1,35 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.device.DeviceAdminService;
+import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.host.HostAdminService;
+import org.onlab.onos.net.host.HostService;
+
+/**
+ * Wipes-out the entire network information base, i.e. devices, links, hosts.
+ */
+@Command(scope = "onos", name = "wipe-out",
+         description = "Wipes-out the entire network information base, i.e. devices, links, hosts")
+public class WipeOutCommand extends ClustersListCommand {
+
+    @Override
+    protected Object doExecute() throws Exception {
+        DeviceAdminService deviceAdminService = get(DeviceAdminService.class);
+        DeviceService deviceService = get(DeviceService.class);
+        for (Device device : deviceService.getDevices()) {
+            deviceAdminService.removeDevice(device.id());
+        }
+
+        HostAdminService hostAdminService = get(HostAdminService.class);
+        HostService hostService = get(HostService.class);
+        for (Host host : hostService.getHosts()) {
+            hostAdminService.removeHost(host.id());
+        }
+        return null;
+    }
+
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 55fec58..4494709 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -58,11 +58,11 @@
 
         <command>
             <action class="org.onlab.onos.cli.net.HostsListCommand"/>
-            <completers>
-                <ref component-id="hostIdCompleter"/>
-            </completers>
         </command>
 
+        <command>
+            <action class="org.onlab.onos.cli.net.WipeOutCommand"/>
+        </command>
     </command-bundle>
 
     <bean id="deviceIdCompleter" class="org.onlab.onos.cli.net.DeviceIdCompleter"/>
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostId.java b/core/api/src/main/java/org/onlab/onos/net/HostId.java
index c406954..170e2a2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostId.java
@@ -42,10 +42,19 @@
      * @param vlanId vlan identifier
      * @return host identifier
      */
-    // FIXME: replace vlanId long with a rich data-type, e.g. VLanId or something like that
     public static HostId hostId(MACAddress mac, VLANID vlanId) {
         // FIXME: use more efficient means of encoding
         return hostId("nic" + ":" + mac + "/" + vlanId);
     }
 
+    /**
+     * Creates a device id using the supplied MAC and default VLAN.
+     *
+     * @param mac mac address
+     * @return host identifier
+     */
+    public static HostId hostId(MACAddress mac) {
+        return hostId(mac, VLANID.vlanId(VLANID.UNTAGGED));
+    }
+
 }
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 724051f..85d5680 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
@@ -4,12 +4,17 @@
 
 /**
  * Service for injecting flow rules into the environment and for obtaining
- * information about flow rules already in the environment.
+ * information about flow rules already in the environment. This implements
+ * semantics of a distributed authoritative flow table where the master copy
+ * of the flow rules lies with the controller and the devices hold only the
+ * 'cached' copy.
  */
 public interface FlowRuleService {
 
     /**
      * Returns the collection of flow entries applied on the specified device.
+     * This will include flow rules which may not yet have been applied to
+     * the device.
      *
      * @param deviceId device identifier
      * @return collection of flow rules
@@ -17,7 +22,9 @@
     Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
 
     /**
-     * Applies the specified flow rules onto their respective devices.
+     * Applies the specified flow rules onto their respective devices. These
+     * flow rules will be retained by the system and re-applied anytime the
+     * device reconnects to the controller.
      *
      * @param flowRules one or more flow rules
      * throws SomeKindOfException that indicates which ones were applied and
@@ -26,7 +33,9 @@
     void applyFlowRules(FlowRule... flowRules);
 
     /**
-     * Removes the specified flow rules from their respective devices.
+     * Removes the specified flow rules from their respective devices. If the
+     * device is not presently connected to the controller, these flow will
+     * be removed once the device reconnects.
      *
      * @param flowRules one or more flow rules
      * throws SomeKindOfException that indicates which ones were removed and
@@ -34,6 +43,10 @@
      */
     void removeFlowRules(FlowRule... flowRules);
 
+
+    // void addInitialFlowContributor(InitialFlowContributor contributor);
+    // void removeInitialFlowContributor(InitialFlowContributor contributor);
+
     /**
      * Adds the specified flow rule listener.
      *
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/Instruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/Instruction.java
index 86aeca5..67e3d67 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/Instruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/Instruction.java
@@ -2,7 +2,6 @@
 
 /**
  * Abstraction of a single traffic treatment step.
- * @param <T> the type parameter for the instruction
  */
 public interface Instruction {
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/Instructions.java b/core/api/src/main/java/org/onlab/onos/net/flow/Instructions.java
index 9681076..cfe1440 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/Instructions.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/Instructions.java
@@ -23,6 +23,7 @@
         return new OutputInstruction(number);
     }
 
+    // TODO: Move these out into separate classes and to flow.instruction package
     public static DropInstruction createDrop() {
         return new DropInstruction();
     }
@@ -30,7 +31,6 @@
     // TODO: add create methods
 
     public static final class DropInstruction implements Instruction {
-
         @Override
         public Type type() {
             return Type.DROP;
@@ -39,7 +39,6 @@
 
 
     public static final class OutputInstruction implements Instruction {
-
         private final PortNumber port;
 
         private OutputInstruction(PortNumber port) {
@@ -54,8 +53,6 @@
         public Type type() {
             return Type.OUTPUT;
         }
-
-
     }
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java b/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
new file mode 100644
index 0000000..d94449a
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
@@ -0,0 +1,17 @@
+package org.onlab.onos.net.host;
+
+import org.onlab.onos.net.HostId;
+
+/**
+ * Service for administering the inventory of end-station hosts.
+ */
+public interface HostAdminService {
+
+    /**
+     * Removes the end-station host with the specified identifier.
+     *
+     * @param hostId host identifier
+     */
+    void removeHost(HostId hostId);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java b/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java
index 48cb084..73fe102 100644
--- a/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java
@@ -43,13 +43,15 @@
 
     /**
      * Blocks the outbound packet from being sent from this point onward.
+     *
      * @return whether the outbound packet is blocked.
      */
     boolean block();
 
     /**
-     * Check whether the outbound packet is blocked.
-     * @return
+     * Indicates whether the outbound packet is handled, i.e. sent or blocked.
+     *
+     * @return true uf the packed is handled
      */
     boolean isHandled();
 
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java
index 6317b14..ece82e1 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java
@@ -139,8 +139,7 @@
     DeviceEvent markOffline(DeviceId deviceId) {
         synchronized (this) {
             Device device = devices.get(deviceId);
-            checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
-            boolean removed = availableDevices.remove(deviceId);
+            boolean removed = device != null && availableDevices.remove(deviceId);
             return !removed ? null :
                     new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
         }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
index b42f1af..3e43dfd 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
@@ -12,6 +12,7 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.host.HostAdminService;
 import org.onlab.onos.net.host.HostDescription;
 import org.onlab.onos.net.host.HostEvent;
 import org.onlab.onos.net.host.HostListener;
@@ -38,7 +39,7 @@
 @Service
 public class SimpleHostManager
         extends AbstractProviderRegistry<HostProvider, HostProviderService>
-        implements HostService, HostProviderRegistry {
+        implements HostService, HostAdminService, HostProviderRegistry {
 
     public static final String HOST_ID_NULL = "Host ID cannot be null";
     private final Logger log = getLogger(getClass());
@@ -124,6 +125,16 @@
         listenerRegistry.removeListener(listener);
     }
 
+    @Override
+    public void removeHost(HostId hostId) {
+        checkNotNull(hostId, HOST_ID_NULL);
+        HostEvent event = store.removeHost(hostId);
+        if (event != null) {
+            log.info("Host {} administratively removed", hostId);
+            post(event);
+        }
+    }
+
     // Personalized host provider service issued to the supplied provider.
     private class InternalHostProviderService
             extends AbstractProviderService<HostProvider>
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java
index 4ea1753..7242642 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java
@@ -16,6 +16,9 @@
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceListener;
+import org.onlab.onos.net.device.DeviceService;
 import org.onlab.onos.net.link.LinkAdminService;
 import org.onlab.onos.net.link.LinkDescription;
 import org.onlab.onos.net.link.LinkEvent;
@@ -49,6 +52,10 @@
             listenerRegistry = new AbstractListenerRegistry<>();
 
     private final SimpleLinkStore store = new SimpleLinkStore();
+    private final DeviceListener deviceListener = new InnerDeviceListener();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
@@ -56,12 +63,14 @@
     @Activate
     public void activate() {
         eventDispatcher.addSink(LinkEvent.class, listenerRegistry);
+        deviceService.addListener(deviceListener);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
         eventDispatcher.removeSink(LinkEvent.class);
+        deviceService.removeListener(deviceListener);
         log.info("Stopped");
     }
 
@@ -140,6 +149,20 @@
         listenerRegistry.removeListener(listener);
     }
 
+    // Auxiliary interceptor for device remove events to prune links that
+    // are associated with the removed device or its port.
+    private class InnerDeviceListener implements DeviceListener {
+        @Override
+        public void event(DeviceEvent event) {
+            if (event.type() == DeviceEvent.Type.DEVICE_REMOVED) {
+                removeLinks(event.subject().id());
+            } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
+                removeLinks(new ConnectPoint(event.subject().id(),
+                                             event.port().number()));
+            }
+        }
+    }
+
     @Override
     protected LinkProviderService createProviderService(LinkProvider provider) {
         return new InternalLinkProviderService(provider);
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/packet/impl/SimplePacketManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/packet/impl/SimplePacketManager.java
index 637ada8..53c3fbc 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/packet/impl/SimplePacketManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/packet/impl/SimplePacketManager.java
@@ -25,10 +25,8 @@
 import org.onlab.onos.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
 
-
 /**
  * Provides a basic implementation of the packet SB &amp; NB APIs.
- *
  */
 @Component(immediate = true)
 @Service
@@ -43,7 +41,6 @@
 
     private final Map<Integer, PacketProcessor> processors = new TreeMap<>();
 
-
     @Activate
     public void activate() {
         log.info("Started");
@@ -62,19 +59,20 @@
 
     @Override
     public void removeProcessor(PacketProcessor processor) {
+        checkNotNull(processor, "Processor cannot be null");
         processors.values().remove(processor);
     }
 
     @Override
     public void emit(OutboundPacket packet) {
+        checkNotNull(packet, "Packet cannot be null");
         final Device device = deviceService.getDevice(packet.sendThrough());
         final PacketProvider packetProvider = getProvider(device.providerId());
         packetProvider.emit(packet);
     }
 
     @Override
-    protected PacketProviderService createProviderService(
-            PacketProvider provider) {
+    protected PacketProviderService createProviderService(PacketProvider provider) {
         return new InternalPacketProviderService(provider);
     }
 
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 6eace8f..3702b0a 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
@@ -22,6 +22,7 @@
 import org.onlab.onos.net.provider.AbstractProvider;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.onos.event.impl.TestEventDispatcher;
+import org.onlab.onos.net.trivial.device.impl.SimpleDeviceManager;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -65,6 +66,7 @@
         admin = mgr;
         registry = mgr;
         mgr.eventDispatcher = new TestEventDispatcher();
+        mgr.deviceService = new SimpleDeviceManager();
         mgr.activate();
 
         service.addListener(listener);
diff --git a/features/features.xml b/features/features.xml
index 10f63bd..d96b2fd 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -39,27 +39,27 @@
 
     <feature name="onos-rest" version="1.0.0"
              description="ONOS REST API components">
-        <feature>onos-core</feature>
+        <feature>onos-api</feature>
         <feature>onos-thirdparty-web</feature>
         <bundle>mvn:org.onlab.onos/onos-rest/1.0.0-SNAPSHOT</bundle>
     </feature>
 
     <feature name="onos-gui" version="1.0.0"
              description="ONOS GUI console components">
-        <feature>onos-core</feature>
+        <feature>onos-api</feature>
         <feature>onos-thirdparty-web</feature>
         <bundle>mvn:org.onlab.onos/onos-gui/1.0.0-SNAPSHOT</bundle>
     </feature>
 
     <feature name="onos-cli" version="1.0.0"
              description="ONOS admin command console components">
-        <feature>onos-core</feature>
+        <feature>onos-api</feature>
         <bundle>mvn:org.onlab.onos/onos-cli/1.0.0-SNAPSHOT</bundle>
     </feature>
 
     <feature name="onos-openflow" version="1.0.0"
             description="ONOS OpenFlow API, Controller &amp; Providers">
-        <feature>onos-core</feature>
+        <feature>onos-api</feature>
         <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
 
         <bundle>mvn:org.onlab.onos/onos-of-api/1.0.0-SNAPSHOT</bundle>
@@ -74,14 +74,14 @@
 
     <feature name="onos-app-tvue" version="1.0.0"
              description="ONOS sample topology viewer application">
-        <feature>onos-core</feature>
+        <feature>onos-api</feature>
         <feature>onos-thirdparty-web</feature>
         <bundle>mvn:org.onlab.onos/onos-app-tvue/1.0.0-SNAPSHOT</bundle>
     </feature>
     
     <feature name="onos-app-fwd" version="1.0.0"
              description="ONOS sample forwarding application">
-        <feature>onos-core</feature>
+        <feature>onos-api</feature>
         <bundle>mvn:org.onlab.onos/onos-app-fwd/1.0.0-SNAPSHOT</bundle>
     </feature>
 
diff --git a/pom.xml b/pom.xml
index 51f1a5a..f461357 100644
--- a/pom.xml
+++ b/pom.xml
@@ -366,7 +366,7 @@
                         <group>
                             <title>Sample Applications</title>
                             <packages>
-                                org.onlab.onos.tvue
+                                org.onlab.onos.tvue:org.onlab.onos.fwd
                             </packages>
                         </group>
                     </groups>
diff --git a/tools/package/bin/onos-ctl b/tools/package/bin/onos-ctl
new file mode 100755
index 0000000..2c6097d
--- /dev/null
+++ b/tools/package/bin/onos-ctl
@@ -0,0 +1,8 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Starts ONOS Apache Karaf container
+#-------------------------------------------------------------------------------
+
+cd $(dirname $0)/../apache-karaf-*/bin
+./karaf "$@"
+
diff --git a/tools/package/package b/tools/package/package
new file mode 100755
index 0000000..870acc6
--- /dev/null
+++ b/tools/package/package
@@ -0,0 +1,59 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Packages ONOS distributable into onos.tar.gz
+#-------------------------------------------------------------------------------
+
+export M2_REPO=${M2_REPO:-~/.m2/repository}
+export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip}
+export KARAF_DIST=$(basename $KARAF_ZIP .zip)
+
+export ONOS_VERSION=${ONOS_VERSION:-1.0.0-SNAPSHOT}
+export ONOS_STAGE_ROOT=${ONOS_STAGE_ROOT:-/tmp}
+export ONOS_BITS=onos-$ONOS_VERSION
+export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS
+
+# Bail on any errors
+set -e
+
+rm -fr $ONOS_STAGE # Remove this when package script is completed
+
+# Make sure we have the original apache karaf bits first
+[ ! -d $M2_REPO ] && echo "M2 repository $M2_REPO not found" && exit 1
+[ ! -f $KARAF_ZIP ] && echo "Apache Karaf bits $KARAF_ZIP not found" && exit 1
+[ -d $ONOS_STAGE ] && echo "ONOS stage $ONOS_STAGE already exists" && exit 1
+
+# Create the stage directory and warp into it
+mkdir -p $ONOS_STAGE
+cd $ONOS_STAGE
+
+# Unroll the Apache Karaf bits and make the ONOS top-level directories.
+unzip $KARAF_ZIP
+mkdir bin
+mkdir lib
+
+# Stage the ONOS admin scripts
+cp -r $ONOS_ROOT/tools/package/bin .
+
+# Stage the ONOS bundles
+mkdir -p lib/org/onlab 
+cp -r $M2_REPO/org/onlab lib/org
+
+
+# Patch the Apache Karaf distribution file to point to the lib as maven repo
+#perl -pi.old -e "s|^org.ops4j.pax.url.mvn.repositories= |org.ops4j.pax.url.mvn.repositories= \\\n    file:../../lib, |" $ONOS_STAGE/$KARAF_DIST/etc/org.ops4j.pax.url.mvn.cfg
+
+# 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,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-tvue|' \
+    /tmp/onos-1.0.0-SNAPSHOT/apache-karaf-3.0.1/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/apache-karaf-*/lib
+
+# Now package up the ONOS tar file
+cd $ONOS_STAGE_ROOT
+tar zcf $ONOS_BITS.tar.gz $ONOS_BITS
diff --git a/utils/misc/src/main/java/org/onlab/packet/VLANID.java b/utils/misc/src/main/java/org/onlab/packet/VLANID.java
index d2a09fa..fa59a82 100644
--- a/utils/misc/src/main/java/org/onlab/packet/VLANID.java
+++ b/utils/misc/src/main/java/org/onlab/packet/VLANID.java
@@ -3,6 +3,7 @@
 /**
  * Representation of a VLAN ID.
  */
+// FIXME: This will end-up looking like a constant; we should name it 'VlanId', 'IpAddress', 'MacAddress'.
 public class VLANID {
 
     private final short value;